home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / src / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-04-02  |  68.5 KB  |  2,760 lines

  1. /* menu.c- generic menu, used for root menu, application menus etc.
  2.  *
  3.  *  Window Maker window manager
  4.  * 
  5.  *  Copyright (c) 1997, 1998 Alfredo K. Kojima
  6.  *  Copyright (c) 1997, 1998 Dan Pascu
  7.  * 
  8.  *  This program is free software; you can redistribute it and/or modify
  9.  *  it under the terms of the GNU General Public License as published by
  10.  *  the Free Software Foundation; either version 2 of the License, or
  11.  *  (at your option) any later version.
  12.  *
  13.  *  This program is distributed in the hope that it will be useful,
  14.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  *  GNU General Public License for more details.
  17.  *
  18.  *  You should have received a copy of the GNU General Public License
  19.  *  along with this program; if not, write to the Free Software
  20.  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
  21.  *  USA.
  22.  */
  23.  
  24. #include "wconfig.h"
  25.  
  26. #include <X11/Xlib.h>
  27. #include <X11/Xutil.h>
  28. #include <X11/keysym.h>
  29. #include <stdlib.h>
  30. #include <string.h>
  31. #include <stdio.h>
  32. #include <unistd.h>
  33. #include <ctype.h>
  34. #include <nana.h>
  35.  
  36. #include "WindowMaker.h"
  37. #include "wcore.h"
  38. #include "framewin.h"
  39. #include "menu.h"
  40. #include "actions.h"
  41. #include "funcs.h"
  42. #include "stacking.h"
  43. #include "text.h"
  44.  
  45.  
  46. /****** Global Variables ******/
  47.  
  48. extern Cursor wCursor[WCUR_LAST];
  49.  
  50. extern XContext wWinContext;
  51.  
  52. extern WPreferences wPreferences;
  53.  
  54. #define MOD_MASK wPreferences.modifier_mask
  55.  
  56. #define MENU_SCROLL_STEP  menuScrollParameters[(int)wPreferences.menu_scroll_speed].steps
  57. #define MENU_SCROLL_DELAY menuScrollParameters[(int)wPreferences.menu_scroll_speed].delay
  58.  
  59.  
  60.  
  61. #define MENUW(m)    ((m)->frame->core->width+2*FRAME_BORDER_WIDTH)
  62. #define MENUH(m)    ((m)->frame->core->height+2*FRAME_BORDER_WIDTH)
  63.  
  64.  
  65. /***** Local Stuff ******/
  66.  
  67. static struct {
  68.     int steps;
  69.     int delay;
  70. } menuScrollParameters[5] = {
  71.     {MENU_SCROLL_STEPS_UF, MENU_SCROLL_DELAY_UF},
  72.     {MENU_SCROLL_STEPS_F, MENU_SCROLL_DELAY_F},
  73.     {MENU_SCROLL_STEPS_M, MENU_SCROLL_DELAY_M},
  74.     {MENU_SCROLL_STEPS_S, MENU_SCROLL_DELAY_S},
  75.     {MENU_SCROLL_STEPS_U, MENU_SCROLL_DELAY_U}};
  76.  
  77.  
  78. static void menuMouseDown(WObjDescriptor *desc, XEvent *event);
  79. static void menuExpose(WObjDescriptor *desc, XEvent *event);
  80.  
  81. static void menuTitleDoubleClick(WCoreWindow *sender, void *data, XEvent *event);
  82. static void menuTitleMouseDown(WCoreWindow *sender, void *data, XEvent *event);
  83.  
  84. static void menuCloseClick(WCoreWindow *sender, void *data, XEvent *event);
  85.  
  86. static void updateTexture(WMenu *menu);
  87.  
  88. #ifndef LITE
  89. static int saveMenuRecurs(proplist_t menus, WScreen *scr, WMenu *menu);
  90. static int restoreMenuRecurs(WScreen *scr, proplist_t menus, WMenu *menu, char *path);
  91. #endif /* !LITE */
  92.  
  93. static void selectEntry(WMenu *menu, int entry_no);
  94. static void closeCascade(WMenu *menu);
  95.  
  96.  
  97. /****** Notification Observers ******/
  98.  
  99. static void
  100. appearanceObserver(void *self, WMNotification *notif)
  101. {
  102.     WMenu *menu = (WMenu*)self;
  103.     int flags = (int)WMGetNotificationClientData(notif);
  104.  
  105.     if (!menu->flags.realized)
  106.     return;
  107.     
  108.     if (WMGetNotificationName(notif) == WNMenuAppearanceSettingsChanged) {
  109.     if (flags & WFontSettings) {
  110.         menu->flags.realized = 0;
  111.         wMenuRealize(menu);
  112.     }
  113.     if (flags & WTextureSettings) {
  114.         if (!menu->flags.brother)
  115.         updateTexture(menu);
  116.     }
  117.     if (flags & (WTextureSettings|WColorSettings)) {
  118.         wMenuPaint(menu);
  119.     }
  120.     } else if (menu->flags.titled) {
  121.  
  122.     if (flags & WFontSettings) {
  123.         menu->flags.realized = 0;
  124.         wMenuRealize(menu);
  125.     }
  126.     if (flags & WTextureSettings) {
  127.         menu->frame->flags.need_texture_remake = 1;
  128.     }
  129.     if (flags & (WColorSettings|WTextureSettings)) {
  130.         wFrameWindowPaint(menu->frame);
  131.     }
  132.     }
  133. }
  134.  
  135. /************************************/
  136.  
  137.  
  138. /*
  139.  *----------------------------------------------------------------------
  140.  * wMenuCreate--
  141.  *     Creates a new empty menu with the specified title. If main_menu
  142.  * is True, the created menu will be a main menu, which has some special
  143.  * properties such as being placed over other normal menus.
  144.  *     If title is NULL, the menu will have no titlebar.
  145.  * 
  146.  * Returns:
  147.  *     The created menu.
  148.  *---------------------------------------------------------------------- 
  149.  */
  150. WMenu*
  151. wMenuCreate(WScreen *screen, char *title, int main_menu)
  152. {
  153.     WMenu *menu;
  154.     static int brother=0;
  155.     int tmp, flags;
  156.     
  157.     menu = wmalloc(sizeof(WMenu));
  158.  
  159.     memset(menu, 0, sizeof(WMenu));
  160.  
  161. #ifdef SINGLE_MENULEVEL
  162.     tmp = WMSubmenuLevel;
  163. #else
  164.     tmp = (main_menu ? WMMainMenuLevel : WMSubmenuLevel);
  165. #endif
  166.  
  167.     flags = WFF_SINGLE_STATE|WFF_BORDER;
  168.     if (title) {
  169.     flags |= WFF_TITLEBAR|WFF_RIGHT_BUTTON;
  170.     menu->flags.titled = 1;
  171.     }
  172.     menu->frame =
  173.     wFrameWindowCreate(screen, tmp, 8, 2, 1, 1, &wPreferences.menu_title_clearance, flags,
  174.                screen->menu_title_texture, NULL,
  175.                            screen->menu_title_pixel,
  176. #ifdef DRAWSTRING_PLUGIN
  177.                W_STRING_MTITLE,
  178. #endif
  179.                &screen->menu_title_gc,
  180.                            &screen->menu_title_font);
  181.                
  182.     menu->frame->core->descriptor.parent = menu;
  183.     menu->frame->core->descriptor.parent_type = WCLASS_MENU;
  184.     menu->frame->core->descriptor.handle_mousedown = menuMouseDown;
  185.     
  186.     wFrameWindowHideButton(menu->frame, WFF_RIGHT_BUTTON);
  187.  
  188.     if (title) {
  189.     menu->frame->title = wstrdup(title);
  190.     }
  191.  
  192.     menu->frame->flags.justification = WTJ_LEFT;
  193.  
  194.     menu->frame->rbutton_image = screen->b_pixmaps[WBUT_CLOSE];
  195.  
  196.     menu->entry_no = 0;
  197.     menu->alloced_entries = 0;
  198.     menu->selected_entry = -1;
  199.     menu->entries = NULL;
  200.     
  201.     menu->frame_x = screen->app_menu_x;
  202.     menu->frame_y = screen->app_menu_y;
  203.     
  204.     menu->frame->child = menu;
  205.  
  206.     menu->flags.lowered = 0;
  207.  
  208.     /* create borders */
  209.     if (title) {
  210.     /* setup object descriptors */
  211.     menu->frame->on_mousedown_titlebar = menuTitleMouseDown;
  212.     menu->frame->on_dblclick_titlebar = menuTitleDoubleClick;
  213.     }
  214.     
  215.     menu->frame->on_click_right = menuCloseClick;
  216.     
  217.     
  218.     menu->menu = wCoreCreate(menu->frame->core, 0, menu->frame->top_width,
  219.                  menu->frame->core->width, 10);
  220.  
  221.     menu->menu->descriptor.parent = menu;
  222.     menu->menu->descriptor.parent_type = WCLASS_MENU;
  223.     menu->menu->descriptor.handle_expose = menuExpose;
  224.     menu->menu->descriptor.handle_mousedown = menuMouseDown;
  225.     
  226.     menu->menu_texture_data = None;
  227.  
  228.     XMapWindow(dpy, menu->menu->window);
  229.  
  230.     XFlush(dpy);
  231.     
  232.     if (!brother) {
  233.     brother = 1;
  234.     menu->brother = wMenuCreate(screen, title, main_menu);
  235.     brother = 0;
  236.     menu->brother->flags.brother = 1;
  237.     menu->brother->brother = menu;
  238.     }
  239.     WMAddNotificationObserver(appearanceObserver, menu,
  240.                   WNMenuAppearanceSettingsChanged, menu);
  241.  
  242.     WMAddNotificationObserver(appearanceObserver, menu,
  243.                   WNMenuTitleAppearanceSettingsChanged, menu);
  244.  
  245.  
  246.     return menu;
  247. }
  248.  
  249.  
  250.  
  251.  
  252. WMenu*
  253. wMenuCreateForApp(WScreen *screen, char *title, int main_menu)
  254. {
  255.     WMenu *menu;
  256.     
  257.     menu = wMenuCreate(screen, title, main_menu);
  258.     if (!menu)
  259.       return NULL;
  260.     menu->flags.app_menu = 1;
  261.     menu->brother->flags.app_menu = 1;
  262.  
  263.     return menu;
  264. }
  265.  
  266.  
  267.  
  268. static void
  269. insertEntry(WMenu *menu, WMenuEntry *entry, int index)
  270. {
  271.     int i;
  272.     
  273.     for (i = menu->entry_no-1; i >= index; i--) {
  274.     menu->entries[i]->order++;
  275.     menu->entries[i+1] = menu->entries[i];
  276.     }
  277.     menu->entries[index] = entry;
  278. }
  279.  
  280.  
  281. WMenuEntry*
  282. wMenuInsertCallback(WMenu *menu, int index, char *text,
  283.             void (*callback)(WMenu *menu, WMenuEntry *entry), 
  284.             void *clientdata)
  285. {
  286.     WMenuEntry *entry;
  287.  
  288.     menu->flags.realized = 0;
  289.     menu->brother->flags.realized = 0;
  290.     
  291.     /* reallocate array if it's too small */
  292.     if (menu->entry_no >= menu->alloced_entries) {
  293.     void *tmp;
  294.  
  295.     tmp = wrealloc(menu->entries,
  296.                sizeof(WMenuEntry)*(menu->alloced_entries+5));
  297.  
  298.     menu->entries = tmp;
  299.     menu->alloced_entries += 5;
  300.     
  301.     menu->brother->entries = tmp;
  302.     menu->brother->alloced_entries = menu->alloced_entries;
  303.     }
  304.     entry = wmalloc(sizeof(WMenuEntry));
  305.     memset(entry, 0, sizeof(WMenuEntry));
  306.     entry->flags.enabled = 1;
  307.     entry->text = wstrdup(text);
  308.     entry->cascade = -1;
  309.     entry->clientdata = clientdata;
  310.     entry->callback = callback;
  311.     if (index<0 || index>=menu->entry_no) {
  312.     entry->order = menu->entry_no;
  313.     menu->entries[menu->entry_no] = entry;
  314.     } else {
  315.     entry->order = index;
  316.     insertEntry(menu, entry, index);
  317.     }
  318.     
  319.     menu->entry_no++;
  320.     menu->brother->entry_no = menu->entry_no;
  321.     
  322.     return entry;
  323. }
  324.  
  325.  
  326.  
  327. void
  328. wMenuEntrySetCascade(WMenu *menu, WMenuEntry *entry, WMenu *cascade)
  329. {
  330.     WMenu *brother = menu->brother;
  331.     int i, done;
  332.  
  333.     assert(menu->flags.brother==0);
  334.     
  335.     if (entry->cascade>=0) {
  336.     menu->flags.realized = 0;
  337.     brother->flags.realized = 0;
  338.     }
  339.     
  340.     cascade->parent = menu;
  341.  
  342.     cascade->brother->parent = brother;
  343.  
  344.     done = 0;
  345.     for (i=0; i<menu->cascade_no; i++) {
  346.     if (menu->cascades[i]==NULL) {
  347.         menu->cascades[i] = cascade;
  348.         brother->cascades[i] = cascade->brother;
  349.         done = 1;
  350.         entry->cascade = i;
  351.         break;
  352.     }
  353.     }
  354.     if (!done) {
  355.     entry->cascade = menu->cascade_no;
  356.  
  357.     menu->cascades = wrealloc(menu->cascades, 
  358.                   sizeof(WMenu)*(menu->cascade_no+1));
  359.     menu->cascades[menu->cascade_no++] = cascade;
  360.     
  361.  
  362.     brother->cascades = wrealloc(brother->cascades, 
  363.                      sizeof(WMenu)*(brother->cascade_no+1));
  364.     brother->cascades[brother->cascade_no++] = cascade->brother;
  365.     }
  366.  
  367.  
  368.     if (menu->flags.lowered) {
  369.     
  370.     cascade->flags.lowered = 1;
  371.     ChangeStackingLevel(cascade->frame->core, WMNormalLevel);
  372.     
  373.     cascade->brother->flags.lowered = 1;
  374.     ChangeStackingLevel(cascade->brother->frame->core, WMNormalLevel);
  375.     }
  376.     
  377.     if (!menu->flags.realized)
  378.     wMenuRealize(menu);
  379. }
  380.  
  381.  
  382. void
  383. wMenuEntryRemoveCascade(WMenu *menu, WMenuEntry *entry)
  384. {    
  385.     assert(menu->flags.brother==0);
  386.     
  387.     /* destroy cascade menu */
  388.     if (entry->cascade>=0 && menu->cascades
  389.     && menu->cascades[entry->cascade]!=NULL) {
  390.  
  391.     wMenuDestroy(menu->cascades[entry->cascade], True);
  392.  
  393.     menu->cascades[entry->cascade] = NULL;
  394.     menu->brother->cascades[entry->cascade] = NULL;
  395.  
  396.     entry->cascade = -1;
  397.     }
  398. }
  399.  
  400.  
  401. void
  402. wMenuRemoveItem(WMenu *menu, int index)
  403. {
  404.     int i;
  405.     
  406.     if (menu->flags.brother) {
  407.     wMenuRemoveItem(menu->brother, index);
  408.     return;
  409.     }
  410.     
  411.     if (index>=menu->entry_no) return;
  412.  
  413.     /* destroy cascade menu */
  414.     wMenuEntryRemoveCascade(menu, menu->entries[index]);
  415.     
  416.     /* destroy unshared data */
  417.  
  418.     if (menu->entries[index]->text)
  419.       free(menu->entries[index]->text);
  420.     
  421.     if (menu->entries[index]->rtext)
  422.       free(menu->entries[index]->rtext);
  423.     
  424.     if (menu->entries[index]->free_cdata && menu->entries[index]->clientdata)
  425.       (*menu->entries[index]->free_cdata)(menu->entries[index]->clientdata);
  426.  
  427.     free(menu->entries[index]);
  428.     
  429.     for (i=index; i<menu->entry_no-1; i++) {
  430.     menu->entries[i+1]->order--;
  431.     menu->entries[i]=menu->entries[i+1];
  432.     }
  433.     menu->entry_no--;
  434.     menu->brother->entry_no--;
  435. }
  436.  
  437.  
  438. static Pixmap
  439. renderTexture(WMenu *menu)
  440. {
  441.     RImage *img;
  442.     Pixmap pix;
  443.     int i;
  444.     RColor light;
  445.     RColor dark;
  446.     RColor mid;
  447.     WScreen *scr = menu->menu->screen_ptr;
  448.     WTexture *texture = scr->menu_item_texture;
  449.  
  450.     if (wPreferences.menu_style == MS_NORMAL) {
  451.     img = wTextureRenderImage(texture, menu->menu->width, 
  452.                   menu->entry_height, WREL_MENUENTRY);
  453.     } else {
  454.     img = wTextureRenderImage(texture, menu->menu->width, 
  455.                   menu->menu->height+1, WREL_MENUENTRY);
  456.     }
  457.     if (!img) {
  458.     wwarning(_("could not render texture: %s"), 
  459.          RMessageForError(RErrorCode));
  460.  
  461.     return None;
  462.     }
  463.  
  464.     if (wPreferences.menu_style == MS_SINGLE_TEXTURE) {
  465.     light.alpha = 0;
  466.     light.red = light.green = light.blue = 80;
  467.     
  468.     dark.alpha = 255;
  469.     dark.red = dark.green = dark.blue = 0;
  470.     
  471.     mid.alpha = 0;
  472.     mid.red = mid.green = mid.blue = 40;
  473.     
  474.     for (i = 1; i < menu->entry_no; i++) {        
  475.         ROperateLine(img, RSubtractOperation, 0, i*menu->entry_height-2,
  476.              menu->menu->width-1, i*menu->entry_height-2, &mid);
  477.         
  478.         RDrawLine(img, 0, i*menu->entry_height-1,
  479.               menu->menu->width-1, i*menu->entry_height-1, &dark);
  480.  
  481.         ROperateLine(img, RAddOperation, 0, i*menu->entry_height,
  482.              menu->menu->width-1, i*menu->entry_height, 
  483.              &light);
  484.     }
  485.     }
  486.     if (!RConvertImage(scr->rcontext, img, &pix)) {
  487.     wwarning(_("error rendering image:%s"), RMessageForError(RErrorCode));
  488.     }
  489.     RDestroyImage(img);
  490.  
  491.     return pix;
  492. }
  493.  
  494.  
  495. static void
  496. updateTexture(WMenu *menu)
  497. {
  498.     WScreen *scr = menu->menu->screen_ptr;
  499.  
  500.     /* setup background texture */
  501.     if (scr->menu_item_texture->any.type != WTEX_SOLID) {
  502.     if (!menu->flags.brother) {
  503.         FREE_PIXMAP(menu->menu_texture_data);
  504.  
  505.         menu->menu_texture_data = renderTexture(menu);
  506.  
  507.         XSetWindowBackgroundPixmap(dpy, menu->menu->window,
  508.                        menu->menu_texture_data);
  509.         XClearWindow(dpy, menu->menu->window);
  510.  
  511.         XSetWindowBackgroundPixmap(dpy, menu->brother->menu->window,
  512.                        menu->menu_texture_data);
  513.         XClearWindow(dpy, menu->brother->menu->window);
  514.     }
  515.     } else {
  516.     XSetWindowBackground(dpy, menu->menu->window,
  517.                  scr->menu_item_texture->any.color.pixel);
  518.     XClearWindow(dpy, menu->menu->window);
  519.     }
  520. }
  521.  
  522.  
  523. void 
  524. wMenuRealize(WMenu *menu)
  525. {
  526.     int i;
  527.     int width, rwidth, mrwidth, mwidth;
  528.     int theight, twidth, eheight;
  529.     WScreen *scr = menu->frame->screen_ptr;
  530.     static int brother_done=0;
  531.     int flags;
  532.  
  533.     if (!brother_done) {
  534.     brother_done = 1;
  535.     wMenuRealize(menu->brother);
  536.     brother_done = 0;
  537.     }
  538.  
  539.     flags = WFF_SINGLE_STATE|WFF_BORDER;
  540.     if (menu->flags.titled)
  541.     flags |= WFF_TITLEBAR|WFF_RIGHT_BUTTON;
  542.  
  543.     wFrameWindowUpdateBorders(menu->frame, flags);
  544.  
  545.     if (menu->flags.titled) {
  546.     twidth = WMWidthOfString(scr->menu_title_font, menu->frame->title,
  547.                  strlen(menu->frame->title));
  548.         theight = menu->frame->top_width;
  549.         twidth += theight + (wPreferences.new_style ? 16 : 8);
  550.     } else {
  551.     twidth = 0;
  552.     theight = 0;
  553.     }
  554.     eheight = WMFontHeight(scr->menu_entry_font) + 6 + wPreferences.menu_text_clearance * 2;
  555.     menu->entry_height = eheight;
  556.     mrwidth = 0;
  557.     mwidth = 0;
  558.     for (i=0; i<menu->entry_no; i++) {
  559.     char *text;
  560.  
  561.     /* search widest text */
  562.     text = menu->entries[i]->text;
  563.     width = WMWidthOfString(scr->menu_entry_font, text, strlen(text))+10;
  564.  
  565.     if (menu->entries[i]->flags.indicator) {
  566.         width += MENU_INDICATOR_SPACE;
  567.     }
  568.  
  569.     if (width > mwidth)
  570.         mwidth = width;
  571.     
  572.     /* search widest text on right */
  573.     text = menu->entries[i]->rtext;
  574.     if (text)
  575.         rwidth = WMWidthOfString(scr->menu_entry_font, text, strlen(text))
  576.         + 5;
  577.     else if (menu->entries[i]->cascade>=0)
  578.         rwidth = 16;
  579.     else
  580.         rwidth = 4;
  581.  
  582.     if (rwidth > mrwidth)
  583.         mrwidth = rwidth;
  584.     }
  585.     mwidth += mrwidth;
  586.     
  587.     if (mwidth < twidth)
  588.     mwidth = twidth;
  589.  
  590.     
  591.     wCoreConfigure(menu->menu, 0, theight, mwidth, menu->entry_no*eheight -1);
  592.  
  593.     wFrameWindowResize(menu->frame, mwidth, menu->entry_no*eheight-1
  594.                + menu->frame->top_width + menu->frame->bottom_width);
  595.  
  596.  
  597.     updateTexture(menu);
  598.  
  599.     menu->flags.realized = 1;
  600.  
  601.     if (menu->flags.mapped)
  602.     wMenuPaint(menu);
  603.     if (menu->brother->flags.mapped)
  604.     wMenuPaint(menu->brother);
  605. }
  606.  
  607.  
  608. void
  609. wMenuDestroy(WMenu *menu, int recurse)
  610. {
  611.     int i;
  612.  
  613.     WMRemoveNotificationObserver(menu);
  614.     
  615.     /* remove any pending timers */
  616.     if (menu->timer)
  617.     WMDeleteTimerHandler(menu->timer);
  618.     menu->timer = NULL;
  619.     
  620.     /* call destroy handler */
  621.     if (menu->on_destroy)
  622.     (*menu->on_destroy)(menu);
  623.     
  624.     /* Destroy items if this menu own them. If this is the "brother" menu,
  625.      * leave them alone as it is shared by them.
  626.      */
  627.     if (!menu->flags.brother) {
  628.     for (i=0; i<menu->entry_no; i++) {
  629.  
  630.         free(menu->entries[i]->text);
  631.  
  632.         if (menu->entries[i]->rtext)
  633.           free(menu->entries[i]->rtext);
  634. #ifdef USER_MENU
  635.  
  636.         if (menu->entries[i]->instances){
  637.           PLRelease(menu->entries[i]->instances);
  638.         }
  639. #endif /* USER_MENU */
  640.  
  641.         if (menu->entries[i]->free_cdata && menu->entries[i]->clientdata) {
  642.         (*menu->entries[i]->free_cdata)(menu->entries[i]->clientdata);
  643.         }
  644.         free(menu->entries[i]);
  645.     }
  646.  
  647.     if (recurse) {
  648.         for (i=0; i<menu->cascade_no; i++) {
  649.         if (menu->cascades[i]) {
  650.             if (menu->cascades[i]->flags.brother)
  651.               wMenuDestroy(menu->cascades[i]->brother, recurse);
  652.             else
  653.               wMenuDestroy(menu->cascades[i], recurse);
  654.         }
  655.         }
  656.     }
  657.     
  658.     if (menu->entries)
  659.         free(menu->entries);
  660.     
  661.     }
  662.  
  663.     FREE_PIXMAP(menu->menu_texture_data);
  664.     
  665.     if (menu->cascades)
  666.       free(menu->cascades);
  667.  
  668.     wCoreDestroy(menu->menu);
  669.     wFrameWindowDestroy(menu->frame);
  670.     
  671.     /* destroy copy of this menu */
  672.     if (!menu->flags.brother && menu->brother)
  673.     wMenuDestroy(menu->brother, False);
  674.     
  675.     free(menu);
  676. }
  677.  
  678.  
  679. #define F_NORMAL    0
  680. #define F_TOP        1
  681. #define F_BOTTOM    2
  682. #define F_NONE        3
  683.  
  684. static void
  685. drawFrame(WScreen *scr, Window win, int y, int w, int h, int type)
  686. {
  687.     XSegment segs[2];
  688.     int i;
  689.  
  690.     i = 0;
  691.     segs[i].x1 = segs[i].x2 = w-1;
  692.     segs[i].y1 = y;
  693.     segs[i].y2 = y + h - 1;
  694.     i++;
  695.     if (type != F_TOP && type != F_NONE) {
  696.     segs[i].x1 = 1;
  697.     segs[i].y1 = segs[i].y2 = y + h-2;
  698.     segs[i].x2 = w-1;
  699.     i++;
  700.     }
  701.     XDrawSegments(dpy, win, scr->menu_item_auxtexture->dim_gc, segs, i);
  702.  
  703.     i = 0;
  704.     segs[i].x1 = 0;
  705.     segs[i].y1 = y;
  706.     segs[i].x2 = 0;
  707.     segs[i].y2 = y + h - 1;
  708.     i++;
  709.     if (type != F_BOTTOM && type != F_NONE) {
  710.     segs[i].x1 = 0;
  711.     segs[i].y1 = y;
  712.     segs[i].x2 = w-1;
  713.     segs[i].y2 = y;
  714.     i++;
  715.     }
  716.     XDrawSegments(dpy, win, scr->menu_item_auxtexture->light_gc, segs, i);
  717.  
  718.     if (type != F_TOP && type != F_NONE)
  719.     XDrawLine(dpy, win, scr->menu_item_auxtexture->dark_gc, 0, y+h-1,
  720.           w-1, y+h-1);
  721. }
  722.  
  723.  
  724. static void
  725. paintEntry(WMenu *menu, int index, int selected)
  726. {
  727.     int x, y, w, h, tw;
  728.     int type;
  729.     GC light, dim, dark, textGC;
  730.     WScreen *scr=menu->frame->screen_ptr;
  731.     Window win = menu->menu->window;
  732.     WMenuEntry *entry=menu->entries[index];
  733.  
  734.     if (!menu->flags.realized) return;
  735.     h = menu->entry_height;
  736.     w = menu->menu->width;
  737.     y = index * h;
  738.  
  739.     light = scr->menu_item_auxtexture->light_gc;
  740.     dim = scr->menu_item_auxtexture->dim_gc;
  741.     dark = scr->menu_item_auxtexture->dark_gc;
  742.  
  743.     if (wPreferences.menu_style == MS_FLAT && menu->entry_no > 1) {
  744.     if (index == 0)
  745.         type = F_TOP;
  746.     else if (index == menu->entry_no - 1)
  747.         type = F_BOTTOM;
  748.     else
  749.         type = F_NONE;
  750.     } else {
  751.     type = F_NORMAL;
  752.     }
  753.     
  754.     /* paint background */
  755.     if (selected) {
  756.     XSetForeground(dpy, scr->select_menu_gc, scr->select_pixel);
  757.     XFillRectangle(dpy, win, scr->select_menu_gc, 1, y+1, w-2, h-3);
  758.     if (scr->menu_item_texture->any.type == WTEX_SOLID)
  759.         drawFrame(scr, win, y, w, h, type);
  760.     } else { 
  761.     if (scr->menu_item_texture->any.type == WTEX_SOLID) {
  762.         XClearArea(dpy, win, 0, y + 1, w - 1, h - 3, False);
  763.         /* draw the frame */
  764.         drawFrame(scr, win, y, w, h, type);
  765.     } else {
  766.         XClearArea(dpy, win, 0, y, w, h, False);
  767.     }
  768.     }
  769.  
  770.     if (selected) {
  771.     textGC = scr->select_menu_gc;
  772.     if (entry->flags.enabled) 
  773.         XSetForeground(dpy, textGC, scr->select_text_pixel);
  774.     else
  775.         XSetForeground(dpy, textGC, scr->dtext_pixel);
  776.     } else if (!entry->flags.enabled) {
  777.     textGC = scr->disabled_menu_entry_gc;
  778.     } else { 
  779.     textGC = scr->menu_entry_gc;
  780.     }
  781.     /* draw text */    
  782.     x = 5;
  783.     if (entry->flags.indicator)
  784.     x += MENU_INDICATOR_SPACE + 2;
  785.  
  786.     WMDrawString(scr->wmscreen, win, textGC, scr->menu_entry_font, 
  787.          x, 3 + y + wPreferences.menu_text_clearance, entry->text, strlen(entry->text));
  788.  
  789.     if (entry->cascade>=0) {
  790.     /* draw the cascade indicator */
  791.     XDrawLine(dpy,win,dim, w-11, y+6, w-6, y+h/2-1);
  792.     XDrawLine(dpy,win,light, w-11, y+h-8, w-6, y+h/2-1);
  793.     XDrawLine(dpy,win,dark, w-12, y+6, w-12, y+h-8);
  794.     }
  795.  
  796.     /* draw indicator */
  797.     if (entry->flags.indicator && entry->flags.indicator_on) {
  798.     int iw, ih;
  799.     WPixmap *indicator;
  800.  
  801.     
  802.     switch (entry->flags.indicator_type) {
  803.      case MI_CHECK:
  804.         indicator = scr->menu_check_indicator;
  805.         break;
  806.      case MI_MINIWINDOW:
  807.         indicator = scr->menu_mini_indicator;
  808.         break;
  809.      case MI_HIDDEN:
  810.         indicator = scr->menu_hide_indicator;
  811.         break;
  812.      case MI_SHADED:
  813.         indicator = scr->menu_shade_indicator;
  814.         break;
  815.      case MI_DIAMOND:
  816.      default:
  817.         indicator = scr->menu_radio_indicator;
  818.         break;
  819.     }
  820.     
  821.     iw = indicator->width;
  822.     ih = indicator->height;
  823.     XSetClipMask(dpy, scr->copy_gc, indicator->mask);
  824.     XSetClipOrigin(dpy, scr->copy_gc, 5, y+(h-ih)/2);
  825.     if (selected)
  826.         XSetForeground(dpy, scr->copy_gc, scr->black_pixel);
  827.     else
  828.         XSetForeground(dpy, scr->copy_gc, scr->mtext_pixel);
  829.     XFillRectangle(dpy, win, scr->copy_gc, 5, y+(h-ih)/2, iw, ih);
  830.     /*
  831.     XCopyArea(dpy, indicator->image, win, scr->copy_gc, 0, 0,
  832.           iw, ih, 5, y+(h-ih)/2);
  833.      */
  834.     XSetClipOrigin(dpy, scr->copy_gc, 0, 0);
  835.     }
  836.  
  837.     /* draw right text */
  838.     
  839.     if (entry->rtext && entry->cascade<0) {
  840.     tw = WMWidthOfString(scr->menu_entry_font, entry->rtext,
  841.                  strlen(entry->rtext));
  842.  
  843.     WMDrawString(scr->wmscreen, win, textGC, scr->menu_entry_font, w-6-tw, 
  844.              y + 3 + wPreferences.menu_text_clearance, entry->rtext, strlen(entry->rtext));
  845.     }
  846. }
  847.  
  848.  
  849. static void
  850. move_menus(WMenu *menu, int x, int y)
  851. {
  852.     while (menu->parent) {
  853.     menu = menu->parent;
  854.     x -= MENUW(menu);
  855.     if (!wPreferences.align_menus && menu->selected_entry>=0) {
  856.         y -= menu->selected_entry*menu->entry_height;
  857.     }
  858.     }
  859.     wMenuMove(menu, x, y, True);
  860. }
  861.  
  862. static void
  863. makeVisible(WMenu *menu)
  864. {
  865.     WScreen *scr = menu->frame->screen_ptr;
  866.     int x1, y1, x2, y2, new_x, new_y, move;
  867.     
  868.     if (menu->entry_no<0) return;
  869.     
  870.     x1 = menu->frame_x;
  871.     y1 = menu->frame_y+menu->frame->top_width
  872.     + menu->selected_entry*menu->entry_height;
  873.     x2 = x1 + MENUW(menu);
  874.     y2 = y1 + menu->entry_height;
  875.     
  876.     new_x = x1;
  877.     new_y = y1;
  878.     move = 0;
  879.  
  880.     if (x1 < 0) {
  881.     new_x = 0;
  882.     move = 1;
  883.     } else if (x2 >= scr->scr_width) {
  884.     new_x = scr->scr_width - MENUW(menu) - 1;
  885.     move = 1;
  886.     }
  887.  
  888.     if (y1 < 0) {
  889.     new_y = 0;
  890.     move = 1;
  891.     } else if (y2 >= scr->scr_height) {
  892.     new_y = scr->scr_height - menu->entry_height - 1;
  893.     move = 1;
  894.     }
  895.  
  896.     new_y = new_y - menu->frame->top_width
  897.     - menu->selected_entry*menu->entry_height;
  898.     move_menus(menu, new_x, new_y);
  899. }
  900.  
  901.  
  902. static int
  903. check_key(WMenu *menu, XKeyEvent *event)
  904. {
  905.     int i, ch, s;
  906.     char buffer[32];
  907.     
  908.     if (XLookupString(event, buffer, 32, NULL, NULL)<1)
  909.     return -1;
  910.     
  911.     ch = toupper(buffer[0]);
  912.     
  913.     s = (menu->selected_entry>=0 ? menu->selected_entry+1 : 0);
  914.     
  915. again:
  916.     for (i=s; i<menu->entry_no; i++) {
  917.     if (ch==toupper(menu->entries[i]->text[0])) {
  918.         return i;
  919.     }
  920.     }
  921.     /* no match. Retry from start, if previous started from a selected entry */
  922.     if (s!=0) {
  923.     s = 0;
  924.     goto again;
  925.     }    
  926.     return -1;
  927. }
  928.  
  929.  
  930. static int
  931. keyboardMenu(WMenu *menu)
  932. {
  933.     XEvent event;
  934.     KeySym ksym=NoSymbol;
  935.     int done=0;
  936.     int index;
  937.     WMenuEntry *entry;
  938.     int old_pos_x = menu->frame_x;
  939.     int old_pos_y = menu->frame_y;
  940.     int new_x = old_pos_x, new_y = old_pos_y;
  941.     int scr_width = menu->frame->screen_ptr->scr_width;
  942.     int scr_height = menu->frame->screen_ptr->scr_height;
  943.  
  944.     if (menu->flags.editing)
  945.     return False;
  946.  
  947.  
  948.     XGrabKeyboard(dpy, menu->frame->core->window, True, GrabModeAsync,
  949.           GrabModeAsync, CurrentTime);
  950.  
  951.     if (menu->frame_y+menu->frame->top_width >= scr_height)
  952.     new_y = scr_height - menu->frame->top_width;
  953.     
  954.     if (menu->frame_x+MENUW(menu) >= scr_width)
  955.     new_x = scr_width-MENUW(menu)-1;
  956.     
  957.     move_menus(menu, new_x, new_y);
  958.  
  959.     while (!done && menu->flags.mapped) {
  960.     XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
  961.     WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonPressMask
  962.             |ButtonReleaseMask|KeyPressMask|KeyReleaseMask
  963.             |SubstructureNotifyMask, &event);
  964.  
  965.     switch (event.type) {
  966.      case KeyPress:
  967.         ksym = XLookupKeysym(&event.xkey, 0);
  968.         switch (ksym) {
  969.          case XK_Escape:
  970.         done = 1;
  971.         break;
  972.  
  973.          case XK_Home:
  974.          case XK_KP_Home:
  975.         selectEntry(menu, 0);
  976.         makeVisible(menu);
  977.         break;
  978.         
  979.          case XK_End:
  980.          case XK_KP_End:
  981.         selectEntry(menu, menu->entry_no-1);
  982.         makeVisible(menu);
  983.         break;
  984.         
  985.          case XK_Up:
  986. #ifdef ARROWLESS_KBD
  987.          case XK_k:
  988. #endif
  989.          case XK_KP_Up:
  990.         if (menu->selected_entry <= 0)
  991.                     selectEntry(menu, menu->entry_no-1);
  992.         else
  993.             selectEntry(menu, menu->selected_entry-1);
  994.         makeVisible(menu);
  995.         break;
  996.         
  997.          case XK_Down:
  998. #ifdef ARROWLESS_KBD
  999.          case XK_j:
  1000. #endif
  1001.          case XK_KP_Down:
  1002.         if (menu->selected_entry<0) 
  1003.                     selectEntry(menu, 0);
  1004.                 else if (menu->selected_entry == menu->entry_no-1)
  1005.                     selectEntry(menu, 0);
  1006.         else if (menu->selected_entry < menu->entry_no-1)
  1007.             selectEntry(menu, menu->selected_entry+1);
  1008.         makeVisible(menu);
  1009.         break;
  1010.         
  1011.          case XK_Right:
  1012. #ifdef ARROWLESS_KBD
  1013.          case XK_l:
  1014. #endif
  1015.          case XK_KP_Right:
  1016.         if (menu->selected_entry>=0) {
  1017.             WMenuEntry *entry;
  1018.             entry = menu->entries[menu->selected_entry];
  1019.  
  1020.             if (entry->cascade >= 0 && menu->cascades
  1021.             && menu->cascades[entry->cascade]->entry_no > 0) {
  1022.  
  1023.             XUngrabKeyboard(dpy, CurrentTime);
  1024.  
  1025.             selectEntry(menu->cascades[entry->cascade], 0);
  1026.             if (!keyboardMenu(menu->cascades[entry->cascade]))
  1027.                 done = 1;
  1028.  
  1029.             XGrabKeyboard(dpy, menu->frame->core->window, True,
  1030.                       GrabModeAsync, GrabModeAsync,
  1031.                       CurrentTime);
  1032.             }
  1033.         }
  1034.         break;
  1035.  
  1036.          case XK_Left:
  1037. #ifdef ARROWLESS_KBD
  1038.          case XK_h:
  1039. #endif
  1040.          case XK_KP_Left:
  1041.         if (menu->parent!=NULL && menu->parent->selected_entry>=0) {
  1042.             selectEntry(menu, -1);
  1043.             move_menus(menu, old_pos_x, old_pos_y);
  1044.             return True;
  1045.         }
  1046.         break;
  1047.         
  1048.          case XK_Return:
  1049.         done = 2;
  1050.         break;
  1051.         
  1052.          default:
  1053.         index = check_key(menu, &event.xkey);
  1054.         if (index>=0) {
  1055.             selectEntry(menu, index);
  1056.         }
  1057.         }
  1058.         break;
  1059.         
  1060.      default:
  1061.         if (event.type==ButtonPress)
  1062.         done = 1;
  1063.         
  1064.         WMHandleEvent(&event);
  1065.     }
  1066.     }
  1067.  
  1068.     XUngrabKeyboard(dpy, CurrentTime);
  1069.  
  1070.     if (done==2 && menu->selected_entry>=0) {
  1071.     entry = menu->entries[menu->selected_entry];
  1072.     } else {
  1073.     entry = NULL;
  1074.     }
  1075.  
  1076.     if (entry && entry->callback!=NULL && entry->flags.enabled
  1077.     && entry->cascade < 0) {
  1078. #if (MENU_BLINK_COUNT > 0)
  1079.     int sel = menu->selected_entry;
  1080.     int i;
  1081.     
  1082.     for (i=0; i<MENU_BLINK_COUNT; i++) {
  1083.         paintEntry(menu, sel, False);
  1084.         XSync(dpy, 0);
  1085.         wusleep(MENU_BLINK_DELAY);
  1086.         paintEntry(menu, sel, True);
  1087.         XSync(dpy, 0);
  1088.         wusleep(MENU_BLINK_DELAY);
  1089.     }
  1090. #endif
  1091.     selectEntry(menu, -1);
  1092.  
  1093.     if (!menu->flags.buttoned) {
  1094.         wMenuUnmap(menu);
  1095.         move_menus(menu, old_pos_x, old_pos_y);
  1096.     }
  1097.     closeCascade(menu);
  1098.      
  1099.     (*entry->callback)(menu, entry);
  1100.     } else {
  1101.     if (!menu->flags.buttoned) {
  1102.         wMenuUnmap(menu);
  1103.         move_menus(menu, old_pos_x, old_pos_y);
  1104.     }
  1105.     selectEntry(menu, -1);
  1106.     }
  1107.  
  1108.     
  1109.     /* returns True if returning from a submenu to a parent menu,
  1110.      * False if exiting from menu */
  1111.     return False;
  1112. }
  1113.  
  1114.  
  1115. void
  1116. wMenuMapAt(WMenu *menu, int x, int y, int keyboard)
  1117. {
  1118.     int scr_width = menu->frame->screen_ptr->scr_width;
  1119.     int scr_height = menu->frame->screen_ptr->scr_height;
  1120.  
  1121.     if (!menu->flags.realized) {
  1122.     menu->flags.realized=1;
  1123.     wMenuRealize(menu);
  1124.     }
  1125.     if (!menu->flags.mapped) {
  1126.         if (wPreferences.wrap_menus) {
  1127.             if (x<0) x = 0;
  1128.             if (y<0) y = 0;
  1129.             if (x+MENUW(menu) > scr_width)
  1130.                 x = scr_width - MENUW(menu);
  1131.             if (y+MENUH(menu) > scr_height)
  1132.                 y = scr_height - MENUH(menu);
  1133.         }
  1134.  
  1135.         XMoveWindow(dpy, menu->frame->core->window, x, y);
  1136.         menu->frame_x = x;
  1137.         menu->frame_y = y;
  1138.         XMapWindow(dpy, menu->frame->core->window);
  1139.         wRaiseFrame(menu->frame->core);
  1140.         menu->flags.mapped = 1;
  1141.     } else {
  1142.         selectEntry(menu, 0);
  1143.     }
  1144.     
  1145.     if (keyboard)
  1146.     keyboardMenu(menu);
  1147. }
  1148.  
  1149.  
  1150. void
  1151. wMenuMap(WMenu *menu)
  1152. {    
  1153.     if (!menu->flags.realized) {
  1154.     menu->flags.realized=1;
  1155.     wMenuRealize(menu);
  1156.     }
  1157.     if (menu->flags.app_menu && menu->parent==NULL) {
  1158.     menu->frame_x = menu->frame->screen_ptr->app_menu_x;
  1159.     menu->frame_y = menu->frame->screen_ptr->app_menu_y;
  1160.     XMoveWindow(dpy, menu->frame->core->window, menu->frame_x, menu->frame_y);
  1161.     }
  1162.     XMapWindow(dpy, menu->frame->core->window);
  1163.     wRaiseFrame(menu->frame->core);
  1164.     menu->flags.mapped = 1;
  1165. }
  1166.  
  1167.  
  1168. void
  1169. wMenuUnmap(WMenu *menu)
  1170. {
  1171.     int i;
  1172.  
  1173.     XUnmapWindow(dpy, menu->frame->core->window);
  1174.     if (menu->flags.titled && menu->flags.buttoned) {
  1175.     wFrameWindowHideButton(menu->frame, WFF_RIGHT_BUTTON);
  1176.     }
  1177.     menu->flags.buttoned = 0;
  1178.     menu->flags.mapped = 0;
  1179.     menu->flags.open_to_left = 0;
  1180.  
  1181.     for (i=0; i<menu->cascade_no; i++) {
  1182.     if (menu->cascades[i]!=NULL
  1183.         && menu->cascades[i]->flags.mapped 
  1184.         && !menu->cascades[i]->flags.buttoned) {
  1185.         
  1186.         wMenuUnmap(menu->cascades[i]);
  1187.     }
  1188.     }
  1189.     menu->selected_entry = -1;
  1190.  
  1191.  
  1192.  
  1193. void 
  1194. wMenuPaint(WMenu *menu)
  1195.     int i;
  1196.      
  1197.     if (!menu->flags.mapped) {
  1198.     return;
  1199.     }
  1200.  
  1201.     /* paint entries */
  1202.     for (i=0; i<menu->entry_no; i++) {
  1203.     paintEntry(menu, i, i==menu->selected_entry);
  1204.     }
  1205. }
  1206.  
  1207.  
  1208. void
  1209. wMenuSetEnabled(WMenu *menu, int index, int enable)
  1210. {
  1211.     if (index>=menu->entry_no) return;
  1212.     menu->entries[index]->flags.enabled=enable;
  1213.     paintEntry(menu, index, index==menu->selected_entry);
  1214.     paintEntry(menu->brother, index, index==menu->selected_entry);
  1215. }
  1216.  
  1217.  
  1218. /* ====================================================================== */
  1219.  
  1220.  
  1221. static void
  1222. editEntry(WMenu *menu, WMenuEntry *entry)
  1223. {
  1224.     WTextInput *text;
  1225.     XEvent event;
  1226.     WObjDescriptor *desc;
  1227.     char *t;
  1228.     int done = 0;
  1229.     Window old_focus;
  1230.     int old_revert;
  1231.     
  1232.     menu->flags.editing = 1;
  1233.  
  1234.     text = wTextCreate(menu->menu, 1, menu->entry_height * entry->order,
  1235.                menu->menu->width - 2, menu->entry_height - 1);
  1236.  
  1237.     wTextPutText(text, entry->text);
  1238.     XGetInputFocus(dpy, &old_focus, &old_revert);
  1239.     XSetInputFocus(dpy, text->core->window, RevertToNone, CurrentTime);
  1240.     
  1241.     if (XGrabKeyboard(dpy, text->core->window, True, GrabModeAsync,
  1242.               GrabModeAsync, CurrentTime)!=GrabSuccess) {
  1243.     wwarning("could not grab keyboard");
  1244.     wTextDestroy(text);
  1245.     
  1246.     wSetFocusTo(menu->frame->screen_ptr, 
  1247.             menu->frame->screen_ptr->focused_window);
  1248.     return;
  1249.     }
  1250.  
  1251.  
  1252.     while (!done && !text->done) {
  1253.     XSync(dpy, 0);
  1254.     XAllowEvents(dpy, AsyncKeyboard|AsyncPointer, CurrentTime);
  1255.     XSync(dpy, 0);
  1256.     WMNextEvent(dpy, &event);
  1257.  
  1258.     if (XFindContext(dpy, event.xany.window, wWinContext, 
  1259.              (XPointer *)&desc)==XCNOENT)
  1260.         desc = NULL;
  1261.     
  1262.     if ((desc != NULL) && (desc->handle_anything != NULL)) {
  1263.  
  1264.         (*desc->handle_anything)(desc, &event);
  1265.  
  1266.     } else {        
  1267.         switch (event.type) {
  1268.          case ButtonPress:
  1269.         XAllowEvents(dpy, ReplayPointer, CurrentTime);
  1270.         done = 1;
  1271.  
  1272.          default:
  1273.         WMHandleEvent(&event);
  1274.         break;
  1275.         }
  1276.     }
  1277.     }
  1278.  
  1279.     XSetInputFocus(dpy, old_focus, old_revert, CurrentTime);
  1280.  
  1281.     wSetFocusTo(menu->frame->screen_ptr, 
  1282.         menu->frame->screen_ptr->focused_window);
  1283.  
  1284.  
  1285.     t = wTextGetText(text);
  1286.     /* if !t, the user has canceled editing */
  1287.     if (t) {
  1288.     if (entry->text)
  1289.         free(entry->text);
  1290.     entry->text = wstrdup(t);
  1291.     
  1292.     menu->flags.realized = 0;
  1293.     }
  1294.     wTextDestroy(text);
  1295.     
  1296.     XUngrabKeyboard(dpy, CurrentTime);
  1297.     
  1298.     if (t && menu->on_edit)
  1299.     (*menu->on_edit)(menu, entry);
  1300.     
  1301.     menu->flags.editing = 0;
  1302.  
  1303.     if (!menu->flags.realized)
  1304.     wMenuRealize(menu);
  1305. }
  1306.  
  1307.  
  1308. static void
  1309. selectEntry(WMenu *menu, int entry_no)
  1310. {
  1311.     WMenuEntry *entry;
  1312.     WMenu *submenu;
  1313.     int old_entry;
  1314.  
  1315.     if (menu->entries==NULL)
  1316.     return;
  1317.     
  1318.     if (entry_no >= menu->entry_no)
  1319.     return;
  1320.  
  1321.     old_entry = menu->selected_entry;
  1322.     menu->selected_entry = entry_no;
  1323.  
  1324.     if (old_entry!=entry_no) {
  1325.     
  1326.     /* unselect previous entry */
  1327.     if (old_entry>=0) {
  1328.         paintEntry(menu, old_entry, False);
  1329.         entry = menu->entries[old_entry];
  1330.         
  1331.         /* unmap cascade */
  1332.         if (entry->cascade>=0 && menu->cascades) {
  1333.         if (!menu->cascades[entry->cascade]->flags.buttoned) {
  1334.             wMenuUnmap(menu->cascades[entry->cascade]);
  1335.         }
  1336.         }
  1337.     }
  1338.  
  1339.     if (entry_no<0) {
  1340.         menu->selected_entry = -1;
  1341.         return;
  1342.     }
  1343.     entry = menu->entries[entry_no];
  1344.  
  1345.     if (entry->cascade>=0 && menu->cascades && entry->flags.enabled) {
  1346.         /* Callback for when the submenu is opened.
  1347.          */
  1348.         submenu = menu->cascades[entry->cascade];
  1349.         if (submenu && submenu->flags.brother)
  1350.         submenu = submenu->brother;
  1351.  
  1352.         if (entry->callback) {
  1353.         /* Only call the callback if the submenu is not yet mapped.
  1354.          */
  1355.         if (menu->flags.brother) {
  1356.             if (!submenu || !submenu->flags.mapped)
  1357.             (*entry->callback)(menu->brother, entry);
  1358.         } else {
  1359.             if (!submenu || !submenu->flags.buttoned)
  1360.             (*entry->callback)(menu, entry);
  1361.         }
  1362.         }
  1363.  
  1364.         /* the submenu menu might have changed */
  1365.         submenu = menu->cascades[entry->cascade];
  1366.         
  1367.         /* map cascade */
  1368.         if (!submenu->flags.mapped) {
  1369.                 int x, y;
  1370.  
  1371.                 if (!submenu->flags.realized)
  1372.                     wMenuRealize(submenu);
  1373.                 if (wPreferences.wrap_menus) {
  1374.                     if (menu->flags.open_to_left)
  1375.                         submenu->flags.open_to_left = 1;
  1376.  
  1377.                     if (submenu->flags.open_to_left) {
  1378.                         x = menu->frame_x - MENUW(submenu);
  1379.                         if (x<0) {
  1380.                             x = 0;
  1381.                             submenu->flags.open_to_left = 0;
  1382.                         }
  1383.                     } else {
  1384.                         x = menu->frame_x + MENUW(menu);
  1385.  
  1386.                         if (x + MENUW(submenu)
  1387.                 >= menu->frame->screen_ptr->scr_width) {
  1388.  
  1389.                             x = menu->frame_x - MENUW(submenu);
  1390.                             submenu->flags.open_to_left = 1;
  1391.                         }
  1392.                     }
  1393.                 } else {
  1394.                     x = menu->frame_x + MENUW(menu);
  1395.         }
  1396.  
  1397.                 if (wPreferences.align_menus) {
  1398.                     y = menu->frame_y;
  1399.                 } else {
  1400.                     y = menu->frame_y + menu->entry_height*entry_no;
  1401.                     if (menu->flags.titled)
  1402.                         y += menu->frame->top_width;
  1403.                     if (menu->cascades[entry->cascade]->flags.titled)
  1404.                         y -= menu->cascades[entry->cascade]->frame->top_width;
  1405.                 }
  1406.  
  1407.                 wMenuMapAt(menu->cascades[entry->cascade], x, y, False);
  1408.                 menu->cascades[entry->cascade]->parent = menu;
  1409.             } else {
  1410.                 return;
  1411.             }
  1412.         }
  1413.         paintEntry(menu, entry_no, True);
  1414.     }
  1415. }
  1416.  
  1417.  
  1418. static WMenu*
  1419. findMenu(WScreen *scr, int *x_ret, int *y_ret)
  1420. {
  1421.     WMenu *menu;
  1422.     WObjDescriptor *desc;
  1423.     Window root_ret, win, junk_win;
  1424.     int x, y, wx, wy;
  1425.     unsigned int mask;
  1426.     
  1427.     XQueryPointer(dpy, scr->root_win, &root_ret, &win, &x, &y, &wx, &wy,
  1428.           &mask);
  1429.     
  1430.     if (win==None) return NULL;
  1431.     
  1432.     if (XFindContext(dpy, win, wWinContext, (XPointer *)&desc)==XCNOENT)
  1433.       return NULL;
  1434.     
  1435.     if (desc->parent_type == WCLASS_MENU) {
  1436.     menu = (WMenu*)desc->parent;
  1437.     XTranslateCoordinates(dpy, root_ret, menu->menu->window, wx, wy, 
  1438.                   x_ret, y_ret, &junk_win);
  1439.     return menu;
  1440.     }
  1441.     return NULL;
  1442. }
  1443.  
  1444.  
  1445.  
  1446.  
  1447. static void
  1448. closeCascade(WMenu *menu)
  1449. {
  1450.     WMenu *parent=menu->parent;
  1451.  
  1452.     if (menu->flags.brother 
  1453.     || (!menu->flags.buttoned 
  1454.         && (!menu->flags.app_menu||menu->parent!=NULL))) {
  1455.  
  1456.     selectEntry(menu, -1);
  1457.     XSync(dpy, 0);
  1458. #if (MENU_BLINK_DELAY > 2)
  1459.     wusleep(MENU_BLINK_DELAY/2);
  1460. #endif
  1461.     wMenuUnmap(menu);
  1462.     while (parent!=NULL
  1463.            && (parent->parent!=NULL || !parent->flags.app_menu
  1464.            || parent->flags.brother)
  1465.            && !parent->flags.buttoned) {
  1466.         selectEntry(parent, -1);
  1467.         wMenuUnmap(parent);
  1468.         parent = parent->parent;
  1469.     }
  1470.     if (parent)
  1471.       selectEntry(parent, -1);
  1472.     }
  1473. }
  1474.  
  1475.  
  1476. static void
  1477. closeBrotherCascadesOf(WMenu *menu)
  1478. {
  1479.     WMenu *tmp;
  1480.     int i;
  1481.     
  1482.     for (i=0; i<menu->cascade_no; i++) {
  1483.     if (menu->cascades[i]->flags.brother) {
  1484.         tmp = menu->cascades[i];
  1485.     } else {
  1486.         tmp = menu->cascades[i]->brother;
  1487.     }
  1488.     if (tmp->flags.mapped) {
  1489.         selectEntry(tmp->parent, -1);
  1490.         closeBrotherCascadesOf(tmp);
  1491.         break;
  1492.     }
  1493.     }
  1494. }
  1495.  
  1496.  
  1497. #define getEntryAt(menu, x, y)   ((y)<0 ? -1 : (y)/(menu->entry_height))
  1498.  
  1499.  
  1500. static WMenu*
  1501. parentMenu(WMenu *menu)
  1502. {
  1503.     WMenu *parent;
  1504.     WMenuEntry *entry;
  1505.  
  1506.     if (menu->flags.buttoned)
  1507.     return menu;
  1508.  
  1509.     while (menu->parent && menu->parent->flags.mapped) {
  1510.         parent = menu->parent;
  1511.         if (parent->selected_entry < 0)
  1512.             break;
  1513.         entry = parent->entries[parent->selected_entry];
  1514.         if (!entry->flags.enabled || entry->cascade<0 || !parent->cascades ||
  1515.             parent->cascades[entry->cascade] != menu)
  1516.             break;
  1517.         menu = parent;
  1518.         if (menu->flags.buttoned)
  1519.             break;
  1520.     }
  1521.     
  1522.     return menu;
  1523. }
  1524.  
  1525.  
  1526.  
  1527. /*
  1528.  * Will raise the passed menu, if submenu = 0
  1529.  * If submenu > 0 will also raise all mapped submenus
  1530.  * until the first buttoned one
  1531.  * If submenu < 0 will also raise all mapped parent menus
  1532.  * until the first buttoned one
  1533.  */
  1534.  
  1535. static void
  1536. raiseMenus(WMenu *menu, int submenus)
  1537. {
  1538.     WMenu *submenu;
  1539.     int i;
  1540.  
  1541.     if(!menu) return;
  1542.  
  1543.     wRaiseFrame(menu->frame->core);
  1544.  
  1545.     if (submenus>0 && menu->selected_entry>=0) {
  1546.         i = menu->entries[menu->selected_entry]->cascade;
  1547.         if (i>=0 && menu->cascades) {
  1548.             submenu = menu->cascades[i];
  1549.             if (submenu->flags.mapped && !submenu->flags.buttoned)
  1550.                 raiseMenus(submenu, submenus);
  1551.         }
  1552.     }
  1553.     if (submenus<0 && !menu->flags.buttoned &&
  1554.         menu->parent && menu->parent->flags.mapped)
  1555.         raiseMenus(menu->parent, submenus);
  1556. }
  1557.  
  1558.  
  1559. WMenu*
  1560. wMenuUnderPointer(WScreen *screen)
  1561. {
  1562.     WObjDescriptor *desc;
  1563.     Window root_ret, win;
  1564.     int dummy;
  1565.     unsigned int mask;
  1566.  
  1567.     XQueryPointer(dpy, screen->root_win, &root_ret, &win, &dummy, &dummy,
  1568.                   &dummy, &dummy, &mask);
  1569.  
  1570.     if (win==None) return NULL;
  1571.  
  1572.     if (XFindContext(dpy, win, wWinContext, (XPointer *)&desc)==XCNOENT)
  1573.         return NULL;
  1574.  
  1575.     if (desc->parent_type == WCLASS_MENU)
  1576.         return (WMenu *)desc->parent;
  1577.     return NULL;
  1578. }
  1579.  
  1580.  
  1581.  
  1582. #define MIN(a,b)  (((a) > (b)) ? (b) : (a))
  1583.  
  1584.  
  1585. static void
  1586. getPointerPosition(WScreen *scr, int *x, int *y)
  1587. {
  1588.     Window root_ret, win;
  1589.     int wx, wy;
  1590.     unsigned int mask;
  1591.  
  1592.     XQueryPointer(dpy, scr->root_win, &root_ret, &win, x, y, &wx, &wy, &mask);
  1593. }
  1594.  
  1595.  
  1596. static void
  1597. getScrollAmount(WMenu *menu, int *hamount, int *vamount)
  1598. {
  1599.     WScreen *scr = menu->menu->screen_ptr;
  1600.     int menuX1 = menu->frame_x;
  1601.     int menuY1 = menu->frame_y;
  1602.     int menuX2 = menu->frame_x + MENUW(menu);
  1603.     int menuY2 = menu->frame_y + MENUH(menu);
  1604.     int screenW = scr->scr_width;
  1605.     int screenH = scr->scr_height;
  1606.     int xroot, yroot;
  1607.     
  1608.     *hamount = 0;
  1609.     *vamount = 0;
  1610.     
  1611.     getPointerPosition(scr, &xroot, &yroot);
  1612.  
  1613. #ifdef VIRTUAL_DESKTOP
  1614.     if (wPreferences.vedge_thickness) {
  1615.         if (xroot <= wPreferences.vedge_thickness + 1 && menuX1 < wPreferences.vedge_thickness) {
  1616.             /* scroll to the right */
  1617.             *hamount = MIN(MENU_SCROLL_STEP, abs(menuX1));
  1618.  
  1619.         } else if (xroot >= screenW-2-wPreferences.vedge_thickness && menuX2 > screenW-1-wPreferences.vedge_thickness) {
  1620.             /* scroll to the left */
  1621.             *hamount = MIN(MENU_SCROLL_STEP, abs(menuX2-screenW-1));
  1622.  
  1623.             if (*hamount==0)
  1624.                 *hamount = 1;
  1625.  
  1626.             *hamount = -*hamount;
  1627.         }
  1628.     } else
  1629. #endif
  1630.  
  1631.     if (xroot <= 1 && menuX1 < 0) {
  1632.     /* scroll to the right */
  1633.     *hamount = MIN(MENU_SCROLL_STEP, abs(menuX1));
  1634.  
  1635.     } else if (xroot >= screenW-2 && menuX2 > screenW-1) {
  1636.     /* scroll to the left */
  1637.     *hamount = MIN(MENU_SCROLL_STEP, abs(menuX2-screenW-1));
  1638.     
  1639.     if (*hamount==0)
  1640.         *hamount = 1;
  1641.  
  1642.     *hamount = -*hamount;
  1643.     }
  1644.     
  1645.     if (yroot <= 1 && menuY1 < 0) {
  1646.     /* scroll down */
  1647.     *vamount = MIN(MENU_SCROLL_STEP, abs(menuY1));
  1648.         
  1649.     } else if (yroot >= screenH-2 && menuY2 > screenH-1) {
  1650.     /* scroll up */
  1651.     *vamount = MIN(MENU_SCROLL_STEP, abs(menuY2-screenH-2));
  1652.         
  1653.     *vamount = -*vamount;
  1654.     }
  1655. }
  1656.  
  1657.  
  1658. static void
  1659. dragScrollMenuCallback(void *data)
  1660. {
  1661.     WMenu *menu = (WMenu*)data;
  1662.     WScreen *scr = menu->menu->screen_ptr;
  1663.     WMenu *parent = parentMenu(menu);
  1664.     int hamount, vamount;
  1665.     int x, y;
  1666.     int newSelectedEntry;
  1667.     
  1668.     getScrollAmount(menu, &hamount, &vamount);
  1669.     
  1670.     
  1671.     if (hamount != 0 || vamount != 0) {
  1672.     wMenuMove(parent, parent->frame_x + hamount,
  1673.           parent->frame_y + vamount, True);
  1674.     if (findMenu(scr, &x, &y)) {
  1675.         newSelectedEntry = getEntryAt(menu, x, y);
  1676.         selectEntry(menu, newSelectedEntry);
  1677.     } else {
  1678.         /* Pointer fell outside of menu. If the selected entry is
  1679.          * not a submenu, unselect it */
  1680.         if (menu->selected_entry >= 0 
  1681.         && menu->entries[menu->selected_entry]->cascade<0)
  1682.         selectEntry(menu, -1);
  1683.         newSelectedEntry = 0;
  1684.     }
  1685.     
  1686.     /* paranoid check */
  1687.     if (newSelectedEntry >= 0) {
  1688.         /* keep scrolling */
  1689.         menu->timer = WMAddTimerHandler(MENU_SCROLL_DELAY, 
  1690.                         dragScrollMenuCallback, menu);
  1691.     } else {
  1692.         menu->timer = NULL;
  1693.     }
  1694.     } else {
  1695.     /* don't need to scroll anymore */
  1696.     menu->timer = NULL;
  1697.     if (findMenu(scr, &x, &y)) {
  1698.         newSelectedEntry = getEntryAt(menu, x, y);
  1699.         selectEntry(menu, newSelectedEntry);
  1700.     }
  1701.     }
  1702. }
  1703.  
  1704.  
  1705. static void
  1706. scrollMenuCallback(void *data)
  1707. {
  1708.     WMenu *menu = (WMenu*)data;
  1709.     WMenu *parent = parentMenu(menu);
  1710.     int hamount = 0;               /* amount to scroll */
  1711.     int vamount = 0;
  1712.  
  1713.     getScrollAmount(menu, &hamount, &vamount);
  1714.     
  1715.     if (hamount != 0 || vamount != 0) {
  1716.     wMenuMove(parent, parent->frame_x + hamount,
  1717.           parent->frame_y + vamount, True);
  1718.  
  1719.     /* keep scrolling */
  1720.     menu->timer = WMAddTimerHandler(MENU_SCROLL_DELAY, 
  1721.                     scrollMenuCallback, menu);
  1722.     } else {
  1723.     /* don't need to scroll anymore */
  1724.     menu->timer = NULL;
  1725.     }
  1726. }
  1727.  
  1728.  
  1729.  
  1730. #define MENU_SCROLL_BORDER   5
  1731.  
  1732. static int
  1733. isPointNearBoder(WMenu *menu, int x, int y)
  1734. {
  1735.     int menuX1 = menu->frame_x;
  1736.     int menuY1 = menu->frame_y;
  1737.     int menuX2 = menu->frame_x + MENUW(menu);
  1738.     int menuY2 = menu->frame_y + MENUH(menu);
  1739.     int scrXe = menu->menu->screen_ptr->scr_width-1;
  1740.     int scrYe = menu->menu->screen_ptr->scr_height-1;
  1741.     int flag = 0;
  1742.     
  1743.     if (x >= menuX1 && x <= menuX2 && (y < MENU_SCROLL_BORDER
  1744.                        || y > scrYe-MENU_SCROLL_BORDER))
  1745.     flag = 1;
  1746.     else if (y >= menuY1 && y <= menuY2 && (x < MENU_SCROLL_BORDER
  1747.                         || x > scrXe-MENU_SCROLL_BORDER))
  1748.     flag = 1;
  1749.     
  1750.     return flag;
  1751. }
  1752.  
  1753.  
  1754. typedef struct _delay {
  1755.     WWindow *wwin;
  1756.     WMenu *menu;
  1757.     int ox,oy;
  1758. } _delay;
  1759.  
  1760.  
  1761. static void
  1762. _leaving(_delay *dl)
  1763. {
  1764.     wMenuMove(dl->menu, dl->ox, dl->oy, True);
  1765.     dl->menu->jump_back=NULL;
  1766.     dl->menu->menu->screen_ptr->flags.jump_back_pending = 0;
  1767.     free(dl);
  1768. }
  1769.  
  1770.  
  1771. void
  1772. wMenuScroll(WMenu *menu, XEvent *event)
  1773. {
  1774.     WMenu *smenu;
  1775.     WMenu *omenu = parentMenu(menu);
  1776.     WScreen *scr = menu->frame->screen_ptr;
  1777.     int done = 0;
  1778.     int jump_back = 0;
  1779.     int old_frame_x = omenu->frame_x;
  1780.     int old_frame_y = omenu->frame_y;
  1781.     XEvent ev;
  1782.     
  1783.     if (omenu->jump_back)
  1784.         WMDeleteTimerWithClientData(omenu->jump_back);
  1785.  
  1786.  
  1787.     if ((/*omenu->flags.buttoned &&*/ !wPreferences.wrap_menus)
  1788.         || omenu->flags.app_menu) {
  1789.         jump_back = 1;
  1790.     }
  1791.  
  1792.     if (!wPreferences.wrap_menus)
  1793.         raiseMenus(omenu, True);
  1794.     else
  1795.         raiseMenus(menu, False);
  1796.  
  1797.     if (!menu->timer)
  1798.     scrollMenuCallback(menu);    
  1799.  
  1800.     while(!done) {
  1801.         int x, y, on_border, on_x_edge, on_y_edge, on_title;
  1802.  
  1803.         WMNextEvent(dpy, &ev);
  1804.         switch (ev.type) {
  1805.      case EnterNotify:
  1806.             WMHandleEvent(&ev);
  1807.      case MotionNotify:
  1808.             x = (ev.type==MotionNotify) ? ev.xmotion.x_root : ev.xcrossing.x_root;
  1809.             y = (ev.type==MotionNotify) ? ev.xmotion.y_root : ev.xcrossing.y_root;
  1810.  
  1811.         /* on_border is != 0 if the pointer is between the menu
  1812.          * and the screen border and is close enough to the border */
  1813.         on_border = isPointNearBoder(menu, x, y);
  1814.  
  1815.             smenu = wMenuUnderPointer(scr);
  1816.                 
  1817.             if ((smenu==NULL && !on_border) || (smenu && parentMenu(smenu)!=omenu)) {
  1818.                 done = 1;
  1819.                 break;
  1820.             }
  1821.  
  1822.             on_x_edge = x <= 1 || x >= scr->scr_width - 2;
  1823.             on_y_edge = y <= 1 || y >= scr->scr_height - 2;
  1824.             on_border = on_x_edge || on_y_edge;
  1825.  
  1826.             if (!on_border && !jump_back) {
  1827.                 done = 1;
  1828.                 break;
  1829.             }
  1830.  
  1831.             if (menu->timer && (smenu!=menu || (!on_y_edge && !on_x_edge))) {
  1832.                 WMDeleteTimerHandler(menu->timer);
  1833.                 menu->timer = NULL;
  1834.             }
  1835.  
  1836.             if (smenu != NULL)
  1837.                 menu = smenu;
  1838.  
  1839.         if (!menu->timer)
  1840.         scrollMenuCallback(menu);
  1841.             break;
  1842.         case ButtonPress:
  1843.             /* True if we push on title, or drag the omenu to other position */
  1844.             on_title = ev.xbutton.x_root >= omenu->frame_x &&
  1845.                 ev.xbutton.x_root <= omenu->frame_x + MENUW(omenu) &&
  1846.                 ev.xbutton.y_root >= omenu->frame_y &&
  1847.                 ev.xbutton.y_root <= omenu->frame_y + omenu->frame->top_width;
  1848.             WMHandleEvent(&ev);
  1849.             smenu = wMenuUnderPointer(scr);
  1850.             if (smenu == NULL || (smenu && smenu->flags.buttoned && smenu != omenu))
  1851.                 done = 1;
  1852.             else if (smenu==omenu && on_title) {
  1853.                 jump_back = 0;
  1854.                 done = 1;
  1855.             }
  1856.             break;
  1857.         case KeyPress:
  1858.             done = 1;
  1859.         default:
  1860.             WMHandleEvent(&ev);
  1861.             break;
  1862.         }
  1863.     }
  1864.  
  1865.     if (menu->timer) {
  1866.         WMDeleteTimerHandler(menu->timer);
  1867.     menu->timer = NULL;
  1868.     }
  1869.  
  1870.     if (jump_back) {
  1871.         _delay *delayer;
  1872.         if (!omenu->jump_back) {
  1873.             delayer=wmalloc(sizeof(_delay));
  1874.             delayer->menu=omenu;
  1875.             delayer->ox=old_frame_x;
  1876.             delayer->oy=old_frame_y;
  1877.             omenu->jump_back=delayer;
  1878.             scr->flags.jump_back_pending = 1;
  1879.         }
  1880.         else delayer = omenu->jump_back;
  1881.         WMAddTimerHandler(MENU_JUMP_BACK_DELAY,(WMCallback*)_leaving, delayer);
  1882.     }
  1883. }
  1884.  
  1885.  
  1886.  
  1887. static void  
  1888. menuExpose(WObjDescriptor *desc, XEvent *event)
  1889. {
  1890.     wMenuPaint(desc->parent);
  1891. }
  1892.  
  1893. typedef struct {
  1894.     int *delayed_select;
  1895.     WMenu *menu;
  1896.     WMHandlerID magic;
  1897. } delay_data;
  1898.  
  1899.  
  1900. static void
  1901. delaySelection(void *data)
  1902. {
  1903.     delay_data *d = (delay_data*)data;
  1904.     int x, y, entry_no;
  1905.     WMenu *menu;
  1906.  
  1907.     d->magic = NULL;
  1908.  
  1909.     menu = findMenu(d->menu->menu->screen_ptr, &x, &y);
  1910.     if (menu && (d->menu == menu || d->delayed_select)) {
  1911.         entry_no = getEntryAt(menu, x, y);
  1912.         selectEntry(menu, entry_no);
  1913.     }
  1914.     if (d->delayed_select)
  1915.     *(d->delayed_select) = 0;
  1916. }
  1917.  
  1918.  
  1919. static void  
  1920. menuMouseDown(WObjDescriptor *desc, XEvent *event)
  1921. {
  1922.     XButtonEvent *bev = &event->xbutton;
  1923.     WMenu *menu = desc->parent;
  1924.     WMenu *smenu;
  1925.     WScreen *scr=menu->frame->screen_ptr;    
  1926.     WMenuEntry *entry=NULL;
  1927.     XEvent ev;
  1928.     int close_on_exit=0;
  1929.     int done=0;
  1930.     int delayed_select = 0;
  1931.     int entry_no;
  1932.     int x, y;
  1933.     int prevx, prevy;
  1934.     int old_frame_x = 0;
  1935.     int old_frame_y = 0;
  1936.     delay_data d_data = {NULL, NULL, NULL};
  1937.  
  1938.     if (!wPreferences.wrap_menus) {
  1939.         smenu = parentMenu(menu);
  1940.         old_frame_x = smenu->frame_x;
  1941.         old_frame_y = smenu->frame_y;
  1942.     } else if (event->xbutton.window == menu->frame->core->window) {
  1943.         /* This is true if the menu was launched with right click on root window */
  1944.         delayed_select = 1;
  1945.         d_data.delayed_select = &delayed_select;
  1946.         d_data.menu = menu;
  1947.         d_data.magic = WMAddTimerHandler(wPreferences.dblclick_time,
  1948.                                         delaySelection, &d_data);
  1949.     }
  1950.     
  1951.     if (menu->flags.inside_handler) {
  1952.     return;
  1953.     }
  1954.     menu->flags.inside_handler = 1;
  1955.  
  1956.     wRaiseFrame(menu->frame->core);
  1957.     
  1958.     close_on_exit = (bev->send_event || menu->flags.brother);
  1959.  
  1960.     smenu = findMenu(scr, &x, &y);
  1961.     if (!smenu) {
  1962.     x = -1;
  1963.     y = -1;
  1964.     } else {
  1965.     menu = smenu;
  1966.     }
  1967.  
  1968.     if (menu->flags.editing) {
  1969.     goto byebye;
  1970.     }
  1971.     entry_no = getEntryAt(menu, x, y);
  1972.     if (entry_no>=0) {
  1973.     entry = menu->entries[entry_no];
  1974.  
  1975.     if (!close_on_exit && (bev->state & ControlMask) && smenu
  1976.         && entry->flags.editable) {
  1977.         editEntry(smenu, entry);
  1978.         goto byebye;
  1979.     } else if (bev->state & ControlMask) {
  1980.         goto byebye;
  1981.     }
  1982.  
  1983.     if (entry->flags.enabled && entry->cascade>=0 && menu->cascades) {
  1984.         WMenu *submenu = menu->cascades[entry->cascade];
  1985.         /* map cascade */
  1986.             if (submenu->flags.mapped && !submenu->flags.buttoned &&
  1987.                 menu->selected_entry!=entry_no) {
  1988.         wMenuUnmap(submenu);
  1989.             }
  1990.             if (!submenu->flags.mapped && !delayed_select) {
  1991.         selectEntry(menu, entry_no);
  1992.         } else if (!submenu->flags.buttoned) {
  1993.         selectEntry(menu, -1);
  1994.         }
  1995.         
  1996.         } else if (!delayed_select) {
  1997.             selectEntry(menu, entry_no);
  1998.         }
  1999.  
  2000.         if (!wPreferences.wrap_menus && !wPreferences.scrollable_menus) {
  2001.         if (!menu->timer)
  2002.         dragScrollMenuCallback(menu);
  2003.         }
  2004.     }
  2005.     
  2006.     prevx = bev->x_root;
  2007.     prevy = bev->y_root;
  2008.     while (!done) {
  2009.     int x, y;
  2010.  
  2011.     XAllowEvents(dpy, AsyncPointer|SyncPointer, CurrentTime);
  2012.  
  2013.     WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
  2014.             |ButtonPressMask, &ev);
  2015.     switch (ev.type) {
  2016.      case MotionNotify:
  2017.             smenu = findMenu(scr, &x, &y);
  2018.  
  2019.         if (smenu == NULL) {
  2020.         /* moved mouse out of menu */
  2021.         
  2022.         if (!delayed_select && d_data.magic) {
  2023.             WMDeleteTimerHandler(d_data.magic);
  2024.             d_data.magic = NULL;
  2025.         }
  2026.         if (menu==NULL
  2027.             || (menu->selected_entry>=0
  2028.             && menu->entries[menu->selected_entry]->cascade>=0)) {
  2029.             prevx = ev.xmotion.x_root;
  2030.             prevy = ev.xmotion.y_root;
  2031.             
  2032.             break;
  2033.         }
  2034.         selectEntry(menu, -1);
  2035.         menu = smenu;
  2036.         prevx = ev.xmotion.x_root;
  2037.         prevy = ev.xmotion.y_root;
  2038.         break;
  2039.         } else if (menu && menu!=smenu
  2040.                && (menu->selected_entry<0
  2041.                || menu->entries[menu->selected_entry]->cascade<0)) {
  2042.         selectEntry(menu, -1);
  2043.  
  2044.         if (!delayed_select && d_data.magic) {
  2045.             WMDeleteTimerHandler(d_data.magic);
  2046.             d_data.magic = NULL;
  2047.         }
  2048.         } else {
  2049.         
  2050.         /* hysteresis for item selection */
  2051.  
  2052.         /* check if the motion was to the side, indicating that 
  2053.          * the user may want to cross to a submenu */
  2054.         if (!delayed_select && menu) {
  2055.             int dx;
  2056.             Bool moved_to_submenu;/* moved to direction of submenu */
  2057.  
  2058.             dx = abs(prevx - ev.xmotion.x_root);
  2059.  
  2060.             moved_to_submenu = False;
  2061.             if (dx > 0 /* if moved enough to the side */
  2062.             /* maybe a open submenu */
  2063.             && menu->selected_entry>=0 
  2064.             /* moving to the right direction */
  2065.             && (wPreferences.align_menus
  2066.                 || ev.xmotion.y_root >= prevy)) {
  2067.             int index;
  2068.             
  2069.             index = menu->entries[menu->selected_entry]->cascade;
  2070.             if (index>=0) {
  2071.                 if (menu->cascades[index]->frame_x>menu->frame_x) {
  2072.                 if (prevx < ev.xmotion.x_root)
  2073.                     moved_to_submenu = True;
  2074.                 } else {
  2075.                 if (prevx > ev.xmotion.x_root)
  2076.                     moved_to_submenu = True;
  2077.                 }
  2078.             }
  2079.             }
  2080.             
  2081.  
  2082.             if (menu != smenu) {
  2083.             if (d_data.magic) {
  2084.                 WMDeleteTimerHandler(d_data.magic);
  2085.             }
  2086.             d_data.magic = NULL;
  2087.             } else if (moved_to_submenu) {
  2088.             /* while we are moving, postpone the selection */
  2089.             if (d_data.magic) {
  2090.                 WMDeleteTimerHandler(d_data.magic);
  2091.             }
  2092.             d_data.delayed_select = NULL;
  2093.             d_data.menu = menu;
  2094.             d_data.magic = WMAddTimerHandler(MENU_SELECT_DELAY, 
  2095.                              delaySelection,
  2096.                              &d_data);
  2097.             prevx = ev.xmotion.x_root;
  2098.             prevy = ev.xmotion.y_root;
  2099.             break;
  2100.             } else {
  2101.             if (d_data.magic)
  2102.                 WMDeleteTimerHandler(d_data.magic);
  2103.             d_data.magic = NULL;
  2104.             }
  2105.         }
  2106.         }
  2107.         prevx = ev.xmotion.x_root;
  2108.         prevy = ev.xmotion.y_root;
  2109.         if (menu!=smenu) {
  2110.         /* pointer crossed menus */
  2111.         if (menu && menu->timer) {
  2112.             WMDeleteTimerHandler(menu->timer);
  2113.             menu->timer = NULL;
  2114.         }
  2115.         if (smenu)
  2116.             dragScrollMenuCallback(smenu);
  2117.         }
  2118.         menu = smenu;
  2119.         if (!menu->timer)
  2120.         dragScrollMenuCallback(menu);
  2121.         
  2122.             if (!delayed_select) {
  2123.                 entry_no = getEntryAt(menu, x, y);
  2124.                 if (entry_no>=0) {
  2125.                     entry = menu->entries[entry_no];
  2126.                     if (entry->flags.enabled && entry->cascade>=0 &&
  2127.                         menu->cascades) {
  2128.                         WMenu *submenu = menu->cascades[entry->cascade];
  2129.                         if (submenu->flags.mapped && !submenu->flags.buttoned
  2130.                             && menu->selected_entry!=entry_no) {
  2131.                             wMenuUnmap(submenu);
  2132.                         }
  2133.                     }
  2134.                 }
  2135.                 selectEntry(menu, entry_no);
  2136.             }
  2137.         break;
  2138.  
  2139.      case ButtonPress:
  2140.         break;
  2141.  
  2142.      case ButtonRelease:
  2143.         if (ev.xbutton.button == event->xbutton.button)
  2144.         done=1;
  2145.             break;
  2146.  
  2147.      case Expose: 
  2148.             WMHandleEvent(&ev);
  2149.             break;
  2150.      }
  2151.     }
  2152.  
  2153.     if (menu && menu->timer) {
  2154.     WMDeleteTimerHandler(menu->timer);
  2155.     menu->timer = NULL;
  2156.     }
  2157.     if (d_data.magic!=NULL)
  2158.         WMDeleteTimerHandler(d_data.magic);
  2159.     
  2160.     if (menu && menu->selected_entry>=0) {
  2161.     entry = menu->entries[menu->selected_entry];
  2162.     if (entry->callback!=NULL && entry->flags.enabled 
  2163.         && entry->cascade < 0) {
  2164.         /* blink and erase menu selection */
  2165. #if (MENU_BLINK_DELAY > 0)
  2166.         int sel = menu->selected_entry;
  2167.         int i;
  2168.  
  2169.         for (i=0; i<MENU_BLINK_COUNT; i++) {
  2170.         paintEntry(menu, sel, False);
  2171.         XSync(dpy, 0);
  2172.         wusleep(MENU_BLINK_DELAY);
  2173.         paintEntry(menu, sel, True);
  2174.         XSync(dpy, 0);
  2175.         wusleep(MENU_BLINK_DELAY);
  2176.         }
  2177. #endif
  2178.         /* unmap the menu, it's parents and call the callback */
  2179.         if (!menu->flags.buttoned && 
  2180.         (!menu->flags.app_menu||menu->parent!=NULL)) {
  2181.         closeCascade(menu);
  2182.         } else {
  2183.         selectEntry(menu, -1);
  2184.         }
  2185.         (*entry->callback)(menu, entry);
  2186.  
  2187.         /* If the user double clicks an entry, the entry will
  2188.          * be executed twice, which is not good for things like
  2189.          * the root menu. So, ignore any clicks that were generated 
  2190.          * while the entry was being executed */
  2191.         while (XCheckTypedWindowEvent(dpy, menu->menu->window, 
  2192.                       ButtonPress, &ev));
  2193.     } else if (entry->callback!=NULL && entry->cascade<0) {
  2194.         selectEntry(menu, -1);
  2195.     } else {        
  2196.         if (entry->cascade>=0 && menu->cascades
  2197.         && menu->cascades[entry->cascade]->flags.brother) {
  2198.         selectEntry(menu, -1);
  2199.         }
  2200.     }
  2201.     }
  2202.  
  2203.     if (((WMenu*)desc->parent)->flags.brother || close_on_exit || !smenu)
  2204.     closeCascade(desc->parent);
  2205.  
  2206.     /* close the cascade windows that should not remain opened */
  2207.     closeBrotherCascadesOf(desc->parent);
  2208.  
  2209.     if (!wPreferences.wrap_menus)
  2210.         wMenuMove(parentMenu(desc->parent), old_frame_x, old_frame_y, True);
  2211.  
  2212. byebye:    
  2213.     ((WMenu*)desc->parent)->flags.inside_handler = 0;
  2214. }
  2215.  
  2216.  
  2217. void
  2218. wMenuMove(WMenu *menu, int x, int y, int submenus)
  2219. {
  2220.     WMenu *submenu;
  2221.     int i;
  2222.     
  2223.     if (!menu) return;
  2224.     
  2225.     menu->frame_x = x;
  2226.     menu->frame_y = y;
  2227.     XMoveWindow(dpy, menu->frame->core->window, x, y);
  2228.       
  2229.     if (submenus>0 && menu->selected_entry>=0) {
  2230.     i = menu->entries[menu->selected_entry]->cascade;
  2231.     
  2232.     if (i>=0 && menu->cascades) {
  2233.         submenu = menu->cascades[i];
  2234.         if (submenu->flags.mapped && !submenu->flags.buttoned) {
  2235.         if (wPreferences.align_menus) {
  2236.             wMenuMove(submenu, x + MENUW(menu), y, submenus);
  2237.         } else {
  2238.             wMenuMove(submenu, x+ MENUW(menu),
  2239.                   y + submenu->entry_height*menu->selected_entry,
  2240.                   submenus);
  2241.         }
  2242.         }
  2243.     }
  2244.     }
  2245.     if (submenus<0 && menu->parent!=NULL && menu->parent->flags.mapped &&
  2246.     !menu->parent->flags.buttoned) {
  2247.     if (wPreferences.align_menus) {
  2248.         wMenuMove(menu->parent, x - MENUW(menu->parent), y, submenus);
  2249.     } else {
  2250.         wMenuMove(menu->parent, x - MENUW(menu->parent), menu->frame_y
  2251.               - menu->parent->entry_height*menu->parent->selected_entry,
  2252.               submenus);
  2253.     }          
  2254.     }
  2255. }
  2256.  
  2257.  
  2258. static void
  2259. changeMenuLevels(WMenu *menu, int lower)
  2260. {
  2261.     int i;
  2262.  
  2263.     if (!lower) {
  2264.     ChangeStackingLevel(menu->frame->core, (!menu->parent ? WMMainMenuLevel
  2265.                       : WMSubmenuLevel));
  2266.     wRaiseFrame(menu->frame->core);
  2267.     menu->flags.lowered = 0;
  2268.     } else {
  2269.     ChangeStackingLevel(menu->frame->core, WMNormalLevel);
  2270.     wLowerFrame(menu->frame->core);
  2271.     menu->flags.lowered = 1;
  2272.     }
  2273.     for (i=0; i<menu->cascade_no; i++) {
  2274.     if (menu->cascades[i]
  2275.         && !menu->cascades[i]->flags.buttoned
  2276.         && menu->cascades[i]->flags.lowered!=lower) {
  2277.         changeMenuLevels(menu->cascades[i], lower);
  2278.     }
  2279.     }
  2280. }
  2281.  
  2282.  
  2283.  
  2284. static void 
  2285. menuTitleDoubleClick(WCoreWindow *sender, void *data, XEvent *event)
  2286. {
  2287.     WMenu *menu = data;
  2288.     int lower;
  2289.     
  2290.     if (event->xbutton.state & MOD_MASK) {
  2291.     if (menu->flags.lowered) {
  2292.         lower = 0;
  2293.     } else {
  2294.         lower = 1;
  2295.     }
  2296.     changeMenuLevels(menu, lower);
  2297.     }
  2298. }
  2299.  
  2300.  
  2301. static void 
  2302. menuTitleMouseDown(WCoreWindow *sender, void *data, XEvent *event)
  2303. {
  2304.     WMenu *menu = data;
  2305.     WMenu *tmp;
  2306.     XEvent ev;
  2307.     int x=menu->frame_x, y=menu->frame_y;
  2308.     int dx=event->xbutton.x_root, dy=event->xbutton.y_root;
  2309.     int i, lower;
  2310.     Bool started;
  2311.  
  2312.     /* can't touch the menu copy */
  2313.     if (menu->flags.brother)
  2314.     return;
  2315.  
  2316.     if (event->xbutton.button != Button1 && event->xbutton.button != Button2)
  2317.       return;
  2318.  
  2319.     if (event->xbutton.state & MOD_MASK) {
  2320.     wLowerFrame(menu->frame->core);
  2321.     lower = 1;
  2322.     } else {
  2323.     wRaiseFrame(menu->frame->core);
  2324.     lower = 0;
  2325.     }
  2326.     tmp = menu;
  2327.  
  2328.     /* lower/raise all submenus */
  2329.     while (1) {
  2330.     if (tmp->selected_entry>=0 && tmp->cascades
  2331.         && tmp->entries[tmp->selected_entry]->cascade>=0) {
  2332.         tmp = tmp->cascades[tmp->entries[tmp->selected_entry]->cascade];
  2333.         if (!tmp || !tmp->flags.mapped)
  2334.           break;
  2335.         if (lower)
  2336.           wLowerFrame(tmp->frame->core);
  2337.         else
  2338.           wRaiseFrame(tmp->frame->core);
  2339.     } else {
  2340.         break;
  2341.     }
  2342.     }
  2343.  
  2344.     /* tear off the menu if it's a root menu or a cascade 
  2345.      application menu */
  2346.     if (!menu->flags.buttoned && !menu->flags.brother 
  2347.     && (!menu->flags.app_menu||menu->parent!=NULL)) {
  2348.     menu->flags.buttoned=1;
  2349.     wFrameWindowShowButton(menu->frame, WFF_RIGHT_BUTTON);
  2350.     if (menu->parent) {
  2351.         /* turn off selected menu entry in parent menu */
  2352.         selectEntry(menu->parent, -1);
  2353.  
  2354.         /* make parent map the copy in place of the original */
  2355.         for (i=0; i<menu->parent->cascade_no; i++) {
  2356.         if (menu->parent->cascades[i] == menu) {
  2357.             menu->parent->cascades[i] = menu->brother;
  2358.             break;
  2359.         }
  2360.         }
  2361.     }
  2362.     }
  2363.  
  2364.     started = False;
  2365.     while(1) {
  2366.     WMMaskEvent(dpy, ButtonMotionMask|ButtonReleaseMask|ButtonPressMask
  2367.             |ExposureMask, &ev);
  2368.     switch (ev.type) {
  2369.      case MotionNotify:
  2370.         if (started) {
  2371.         x += ev.xmotion.x_root - dx;
  2372.         y += ev.xmotion.y_root - dy;
  2373.         dx = ev.xmotion.x_root;
  2374.         dy = ev.xmotion.y_root;
  2375.         wMenuMove(menu, x, y, True);
  2376.         } else {
  2377.         if (abs(ev.xmotion.x_root - dx) > MOVE_THRESHOLD
  2378.             || abs(ev.xmotion.y_root - dy) > MOVE_THRESHOLD) {
  2379.             started = True;
  2380.             XGrabPointer(dpy, menu->frame->titlebar->window, False, 
  2381.                  ButtonMotionMask|ButtonReleaseMask
  2382.                  |ButtonPressMask,
  2383.                  GrabModeAsync, GrabModeAsync, None, 
  2384.                  wCursor[WCUR_MOVE], CurrentTime);
  2385.         }
  2386.         }
  2387.         break;
  2388.  
  2389.      case ButtonPress:
  2390.         break;
  2391.  
  2392.      case ButtonRelease:
  2393.         if (ev.xbutton.button != event->xbutton.button)
  2394.         break;
  2395.         XUngrabPointer(dpy, CurrentTime);
  2396.         return;
  2397.  
  2398.      default:
  2399.         WMHandleEvent(&ev);
  2400.         break;
  2401.     }
  2402.     }
  2403. }
  2404.  
  2405. /*
  2406.  *----------------------------------------------------------------------
  2407.  * menuCloseClick--
  2408.  *     Handles mouse click on the close button of menus. The menu is
  2409.  * closed when the button is clicked. 
  2410.  * 
  2411.  * Side effects:
  2412.  *     The closed menu is reinserted at it's parent menus
  2413.  * cascade list.
  2414.  *---------------------------------------------------------------------- 
  2415.  */
  2416. static void 
  2417. menuCloseClick(WCoreWindow *sender, void *data, XEvent *event)
  2418. {
  2419.     WMenu *menu = (WMenu*)data;
  2420.     WMenu *parent = menu->parent;
  2421.     int i;
  2422.     
  2423.     if (parent) {
  2424.     for (i=0; i<parent->cascade_no; i++) {
  2425.         /* find the entry that points to the copy */
  2426.         if (parent->cascades[i] == menu->brother) {
  2427.         /* make it point to the original */
  2428.         parent->cascades[i] = menu;
  2429.         menu->parent = parent;
  2430.         break;
  2431.         }
  2432.     }
  2433.     }
  2434.     wMenuUnmap(menu);
  2435. }
  2436.  
  2437.  
  2438. static void
  2439. saveMenuInfo(proplist_t dict, WMenu *menu, proplist_t key)
  2440. {
  2441.     proplist_t value, list;
  2442.     char buffer[256];
  2443.  
  2444.     sprintf(buffer, "%i,%i", menu->frame_x, menu->frame_y);
  2445.     value = PLMakeString(buffer);
  2446.     list = PLMakeArrayFromElements(value, NULL);
  2447.     if (menu->flags.lowered)
  2448.     PLAppendArrayElement(list, PLMakeString("lowered"));
  2449.     PLInsertDictionaryEntry(dict, key, list);
  2450.     PLRelease(value);
  2451.     PLRelease(list);
  2452. }
  2453.  
  2454.  
  2455. void
  2456. wMenuSaveState(WScreen *scr)
  2457. {
  2458.     proplist_t menus, key;
  2459.     int save_menus = 0;
  2460.  
  2461.     menus = PLMakeDictionaryFromEntries(NULL, NULL, NULL);
  2462.  
  2463. #ifndef LITE
  2464.     if (scr->switch_menu && scr->switch_menu->flags.buttoned) {
  2465.     key = PLMakeString("SwitchMenu");
  2466.     saveMenuInfo(menus, scr->switch_menu, key);
  2467.     PLRelease(key);
  2468.         save_menus = 1;
  2469.     }
  2470.  
  2471.     if (saveMenuRecurs(menus, scr, scr->root_menu))
  2472.     save_menus = 1;
  2473.  
  2474. #endif /* !LITE */
  2475.     if (scr->workspace_menu && scr->workspace_menu->flags.buttoned) {
  2476.         key = PLMakeString("WorkspaceMenu");
  2477.     saveMenuInfo(menus, scr->workspace_menu, key);
  2478.         PLRelease(key);
  2479.         save_menus = 1;
  2480.     }
  2481.     
  2482.     if (save_menus) {
  2483.         key = PLMakeString("Menus");
  2484.         PLInsertDictionaryEntry(scr->session_state, key, menus);
  2485.         PLRelease(key);
  2486.     }
  2487.     PLRelease(menus);
  2488. }
  2489.  
  2490.  
  2491. #ifndef LITE
  2492.  
  2493. static Bool
  2494. getMenuPath(WMenu *menu, char *buffer, int bufSize)
  2495. {
  2496.     Bool ok = True;
  2497.     int len = 0;
  2498.  
  2499.     if (!menu->flags.titled || !menu->frame->title[0])
  2500.     return False;
  2501.  
  2502.     len = strlen(menu->frame->title);
  2503.     if (len >= bufSize)
  2504.     return False;
  2505.  
  2506.     if (menu->parent) {
  2507.     ok = getMenuPath(menu->parent, buffer, bufSize - len - 1);
  2508.     if (!ok)
  2509.         return False;
  2510.     }
  2511.  
  2512.     strcat(buffer, "\\");
  2513.     strcat(buffer, menu->frame->title);
  2514.  
  2515.     return True;
  2516. }
  2517.  
  2518.  
  2519. static Bool
  2520. saveMenuRecurs(proplist_t menus, WScreen *scr, WMenu *menu)
  2521. {
  2522.     proplist_t key;
  2523.     int save_menus = 0, i;
  2524.     char buffer[512];
  2525.     Bool ok = True;
  2526.  
  2527.     
  2528.     if (menu->flags.brother)
  2529.     menu = menu->brother;
  2530.  
  2531.     if (menu->flags.buttoned && menu != scr->switch_menu) {
  2532.  
  2533.     buffer[0] = '\0';
  2534.     ok = getMenuPath(menu, buffer, 510);
  2535.  
  2536.     if (ok) {
  2537.         key = PLMakeString(buffer);
  2538.         saveMenuInfo(menus, menu, key);
  2539.         PLRelease(key);        
  2540.         save_menus = 1;
  2541.     }
  2542.     }
  2543.  
  2544.     if (ok) {
  2545.     for (i = 0; i < menu->cascade_no; i++) {
  2546.         if (saveMenuRecurs(menus, scr, menu->cascades[i]))
  2547.         save_menus = 1;
  2548.     }
  2549.     }
  2550.     return save_menus;
  2551. }
  2552. #endif /* !LITE */
  2553.  
  2554.  
  2555. #define COMPLAIN(key) wwarning(_("bad value in menus state info:%s"), key)
  2556.  
  2557.  
  2558. static Bool
  2559. getMenuInfo(proplist_t info, int *x, int *y, Bool *lowered)
  2560. {
  2561.     proplist_t pos;
  2562.     
  2563.     *lowered = False;
  2564.     
  2565.     if (PLIsArray(info)) {
  2566.     proplist_t flags;
  2567.     pos = PLGetArrayElement(info, 0);
  2568.     flags = PLGetArrayElement(info, 1);
  2569.     if (flags != NULL && PLIsString(flags) && PLGetString(flags) != NULL
  2570.         && strcmp(PLGetString(flags), "lowered") == 0) {
  2571.         *lowered = True;
  2572.     }
  2573.     } else {
  2574.     pos = info;
  2575.     }
  2576.  
  2577.     if (pos != NULL && PLIsString(pos)) {
  2578.     if (sscanf(PLGetString(pos), "%i,%i", x, y)!=2)
  2579.         COMPLAIN("Position");
  2580.     } else {
  2581.     COMPLAIN("(position, flags...)");
  2582.     return False;
  2583.     }
  2584.  
  2585.     return True;
  2586. }
  2587.  
  2588.  
  2589. static int
  2590. restoreMenu(WScreen *scr, proplist_t menu, int which)
  2591. {
  2592.     int x, y;
  2593.     Bool lowered = False;
  2594.     WMenu *pmenu = NULL;
  2595.  
  2596.     if (!menu)
  2597.     return False;
  2598.     
  2599.     if (!getMenuInfo(menu, &x, &y, &lowered))
  2600.     return False;
  2601.  
  2602.  
  2603. #ifndef LITE
  2604.     if (which & WSS_SWITCHMENU) {
  2605.         OpenSwitchMenu(scr, x, y, False);
  2606.         pmenu = scr->switch_menu;
  2607.     }
  2608. #endif /* !LITE */
  2609.  
  2610.     if (pmenu) {
  2611.         int width = MENUW(pmenu);
  2612.         int height = MENUH(pmenu);
  2613.         
  2614.     if (lowered) {
  2615.         changeMenuLevels(pmenu, True);
  2616.     }
  2617.     
  2618.         x = (x < -width) ? 0 : x;
  2619.         x = (x > scr->scr_width) ? scr->scr_width - width : x;
  2620.         y = (y < 0) ? 0 : y;
  2621.         y = (y > scr->scr_height) ? scr->scr_height - height : y;
  2622.         wMenuMove(pmenu, x, y, True);
  2623.         pmenu->flags.buttoned = 1;
  2624.         wFrameWindowShowButton(pmenu->frame, WFF_RIGHT_BUTTON);
  2625.         return True;
  2626.     }
  2627.     return False;
  2628. }
  2629.  
  2630.  
  2631. #ifndef LITE
  2632. static int
  2633. restoreMenuRecurs(WScreen *scr, proplist_t menus, WMenu *menu, char *path)
  2634. {
  2635.     proplist_t key, entry;
  2636.     char buffer[512];
  2637.     int i, x, y, res;
  2638.     Bool lowered;
  2639.  
  2640.     if (strlen(path) + strlen(menu->frame->title) > 510)
  2641.     return False;
  2642.  
  2643.     sprintf(buffer, "%s\\%s", path, menu->frame->title);
  2644.     key = PLMakeString(buffer);
  2645.     entry = PLGetDictionaryEntry(menus, key);
  2646.     res = False;
  2647.  
  2648.     if (entry && getMenuInfo(entry, &x, &y, &lowered)) {
  2649.  
  2650.     if (!menu->flags.mapped) {
  2651.         int width = MENUW(menu);
  2652.         int height = MENUH(menu);
  2653.  
  2654.         wMenuMapAt(menu, x, y, False);
  2655.  
  2656.         if (menu->parent) {
  2657.         /* make parent map the copy in place of the original */
  2658.         for (i=0; i<menu->parent->cascade_no; i++) {
  2659.             if (menu->parent->cascades[i] == menu) {
  2660.             menu->parent->cascades[i] = menu->brother;
  2661.             break;
  2662.             }
  2663.         }
  2664.         }
  2665.         if (lowered) {
  2666.         changeMenuLevels(menu, True);
  2667.         }
  2668.         x = (x < -width) ? 0 : x;
  2669.         x = (x > scr->scr_width) ? scr->scr_width - width : x;
  2670.         y = (y < 0) ? 0 : y;
  2671.         y = (y > scr->scr_height) ? scr->scr_height - height : y;
  2672.         wMenuMove(menu, x, y, True);
  2673.         menu->flags.buttoned = 1;
  2674.         wFrameWindowShowButton(menu->frame, WFF_RIGHT_BUTTON);
  2675.         res = True;
  2676.     }
  2677.     }
  2678.     
  2679.     PLRelease(key);
  2680.  
  2681.     for (i=0; i<menu->cascade_no; i++) {
  2682.     if (restoreMenuRecurs(scr, menus, menu->cascades[i], buffer) != False)
  2683.         res = True;
  2684.     }
  2685.  
  2686.     return res;
  2687. }
  2688. #endif /* !LITE */
  2689.  
  2690.  
  2691. void
  2692. wMenuRestoreState(WScreen *scr)
  2693. {
  2694.     proplist_t menus, menu, key, skey;
  2695.  
  2696.     key = PLMakeString("Menus");
  2697.     menus = PLGetDictionaryEntry(scr->session_state, key);
  2698.     PLRelease(key);
  2699.  
  2700.     if (!menus)
  2701.         return;
  2702.  
  2703.     /* restore menus */
  2704.  
  2705.     skey = PLMakeString("SwitchMenu");
  2706.     menu = PLGetDictionaryEntry(menus, skey);
  2707.     PLRelease(skey);
  2708.     restoreMenu(scr, menu, WSS_SWITCHMENU);
  2709.  
  2710. #ifndef LITE
  2711.     if (!scr->root_menu) {
  2712.         OpenRootMenu(scr, scr->scr_width*2, 0, False);
  2713.         wMenuUnmap(scr->root_menu);
  2714.     }
  2715.     restoreMenuRecurs(scr, menus, scr->root_menu, "");
  2716. #endif /* !LITE */
  2717.  
  2718.     PLRelease(skey);
  2719. }
  2720.  
  2721.  
  2722. void
  2723. OpenWorkspaceMenu(WScreen *scr, int x, int y)
  2724. {
  2725.     WMenu *menu, *parent;
  2726.     WMenuEntry *entry;
  2727.  
  2728. #ifndef LITE
  2729.     if (!scr->root_menu) {
  2730.         OpenRootMenu(scr, scr->scr_width*2, 0, False);
  2731.         wMenuUnmap(scr->root_menu);
  2732.     }
  2733. #endif
  2734.     menu = scr->workspace_menu;
  2735.     if (menu) {
  2736.         if (menu->flags.mapped) {
  2737.             if (!menu->flags.buttoned) {
  2738.                 wMenuUnmap(menu);
  2739.                 parent = menu->parent;
  2740.                 if (parent && parent->selected_entry >= 0) {
  2741.                     entry = parent->entries[parent->selected_entry];
  2742.                     if (parent->cascades[entry->cascade] == menu) {
  2743.                         selectEntry(parent, -1);
  2744.                         wMenuMapAt(menu, x, y, False);
  2745.                     }
  2746.                 }
  2747.             } else {
  2748.                 wRaiseFrame(menu->frame->core);
  2749.         wMenuMapCopyAt(menu, x, y);
  2750.             }
  2751.         } else {
  2752.             wMenuMapAt(menu, x, y, False);
  2753.         }
  2754.     }
  2755. }
  2756.  
  2757.  
  2758.